1 /**
2 Copyright: Copyright (c) 2016 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.clientdb.plugin;
7 
8 import std.experimental.logger;
9 import netlib;
10 import pluginlib;
11 
12 import voxelman.core.config;
13 import voxelman.core.events;
14 import voxelman.net.events;
15 import voxelman.core.packets;
16 import voxelman.net.packets;
17 import voxelman.storage.coordinates;
18 
19 import voxelman.command.plugin;
20 import voxelman.eventdispatcher.plugin;
21 import voxelman.net.plugin;
22 import voxelman.world.plugin;
23 
24 import voxelman.clientdb.clientinfo;
25 
26 shared static this()
27 {
28 	pluginRegistry.regServerPlugin(new ClientDb);
29 }
30 
31 final class ClientDb : IPlugin
32 {
33 private:
34 	EventDispatcherPlugin evDispatcher;
35 	NetServerPlugin connection;
36 	ServerWorld serverWorld;
37 
38 public:
39 	ClientInfo*[ClientId] clients;
40 
41 	// IPlugin stuff
42 	mixin IdAndSemverFrom!(voxelman.clientdb.plugininfo);
43 
44 	override void registerResources(IResourceManagerRegistry resmanRegistry) {}
45 	override void preInit() {}
46 	override void init(IPluginManager pluginman)
47 	{
48 		evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
49 		connection = pluginman.getPlugin!NetServerPlugin;
50 		serverWorld = pluginman.getPlugin!ServerWorld;
51 
52 		evDispatcher.subscribeToEvent(&handleClientConnected);
53 		evDispatcher.subscribeToEvent(&handleClientDisconnected);
54 
55 		connection.registerPacketHandler!LoginPacket(&handleLoginPacket);
56 		connection.registerPacketHandler!ViewRadiusPacket(&handleViewRadius);
57 		connection.registerPacketHandler!ClientPositionPacket(&handleClientPosition);
58 
59 		auto commandPlugin = pluginman.getPlugin!CommandPluginServer;
60 		commandPlugin.registerCommand("spawn", &onSpawn);
61 	}
62 
63 	void onSpawn(CommandParams params)
64 	{
65 		ClientInfo* info = clients.get(params.source, null);
66 		if(info is null) return;
67 		info.pos = START_POS;
68 		info.heading = vec2(0,0);
69 		connection.sendTo(params.source, ClientPositionPacket(info.pos, info.heading));
70 		updateObserverVolume(info);
71 	}
72 
73 	bool isLoggedIn(ClientId clientId)
74 	{
75 		ClientInfo* clientInfo = clients[clientId];
76 		return clientInfo.isLoggedIn;
77 	}
78 
79 	string[ClientId] clientNames()
80 	{
81 		string[ClientId] names;
82 		foreach(id, client; clients) {
83 			names[id] = client.name;
84 		}
85 
86 		return names;
87 	}
88 
89 	string clientName(ClientId clientId)
90 	{
91 		import std.string : format;
92 		auto cl = clients.get(clientId, null);
93 		return cl ? cl.name : format("%s", clientId);
94 	}
95 
96 	auto loggedInClients()
97 	{
98 		import std.algorithm : filter, map;
99 		return clients.byKeyValue.filter!(a=>a.value.isLoggedIn).map!(a=>a.value.id);
100 	}
101 
102 	void spawnClient(vec3 pos, vec2 heading, ClientId clientId)
103 	{
104 		ClientInfo* info = clients[clientId];
105 		info.pos = pos;
106 		info.heading = heading;
107 		connection.sendTo(clientId, ClientPositionPacket(pos, heading));
108 		connection.sendTo(clientId, SpawnPacket());
109 		updateObserverVolume(info);
110 	}
111 
112 	void handleClientConnected(ref ClientConnectedEvent event)
113 	{
114 		clients[event.clientId] = new ClientInfo(event.clientId);
115 		connection.sendTo(event.clientId, PacketMapPacket(connection.packetNames));
116 	}
117 
118 	void handleClientDisconnected(ref ClientDisconnectedEvent event)
119 	{
120 		serverWorld.chunkObserverManager.removeObserver(event.clientId);
121 
122 		infof("%s %s disconnected", event.clientId,
123 			clients[event.clientId].name);
124 
125 		connection.sendToAll(ClientLoggedOutPacket(event.clientId));
126 		clients.remove(event.clientId);
127 	}
128 
129 	void updateObserverVolume(ClientInfo* info)
130 	{
131 		if (info.isLoggedIn) {
132 			ChunkWorldPos chunkPos = BlockWorldPos(info.pos);
133 			serverWorld.chunkObserverManager.changeObserverVolume(info.id, chunkPos, info.viewRadius);
134 		}
135 	}
136 
137 	void handleLoginPacket(ubyte[] packetData, ClientId clientId)
138 	{
139 		LoginPacket packet = unpackPacket!LoginPacket(packetData);
140 		ClientInfo* info = clients[clientId];
141 		info.name = packet.clientName;
142 		info.id = clientId;
143 		info.isLoggedIn = true;
144 		spawnClient(info.pos, info.heading, clientId);
145 
146 		infof("%s %s logged in", clientId, clients[clientId].name);
147 
148 		connection.sendTo(clientId, SessionInfoPacket(clientId, clientNames));
149 		connection.sendToAllExcept(clientId, ClientLoggedInPacket(clientId, packet.clientName));
150 
151 		evDispatcher.postEvent(ClientLoggedInEvent(clientId));
152 	}
153 
154 	void handleViewRadius(ubyte[] packetData, ClientId clientId)
155 	{
156 		import std.algorithm : clamp;
157 		auto packet = unpackPacket!ViewRadiusPacket(packetData);
158 		infof("Received ViewRadiusPacket(%s)", packet.viewRadius);
159 		ClientInfo* info = clients[clientId];
160 		info.viewRadius = clamp(packet.viewRadius,
161 			MIN_VIEW_RADIUS, MAX_VIEW_RADIUS);
162 		updateObserverVolume(info);
163 	}
164 
165 	void handleClientPosition(ubyte[] packetData, ClientId clientId)
166 	{
167 		if (isLoggedIn(clientId))
168 		{
169 			auto packet = unpackPacket!ClientPositionPacket(packetData);
170 			ClientInfo* info = clients[clientId];
171 			info.pos = packet.pos;
172 			info.heading = packet.heading;
173 			updateObserverVolume(info);
174 		}
175 	}
176 
177 }