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 }