1 /** 2 Copyright: Copyright (c) 2016-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.session.client; 7 8 import std..string : format; 9 10 import datadriven : EntityId; 11 import netlib; 12 import pluginlib; 13 import voxelman.log; 14 import voxelman.math; 15 16 import voxelman.core.config; 17 import voxelman.core.events; 18 import voxelman.net.events; 19 import voxelman.core.packets; 20 import voxelman.net.packets; 21 22 import voxelman.config.configmanager : ConfigManager, ConfigOption; 23 import voxelman.entity.plugin : EntityComponentRegistry; 24 import voxelman.eventdispatcher.plugin; 25 import voxelman.world.clientworld; 26 import voxelman.net.plugin; 27 import voxelman.graphics.plugin; 28 29 import voxelman.session.components; 30 31 struct ThisClientLoggedInEvent { 32 EntityId thisClientId; 33 } 34 35 final class ClientSession : IPlugin 36 { 37 private: 38 EventDispatcherPlugin evDispatcher; 39 GraphicsPlugin graphics; 40 NetClientPlugin connection; 41 ClientWorld clientWorld; 42 43 ConfigOption nicknameOpt; 44 45 public: 46 EntityId thisSessionId; 47 EntityId thisEntityId; 48 string[EntityId] clientNames; 49 bool isSpawned = false; 50 51 // IPlugin stuff 52 mixin IdAndSemverFrom!"voxelman.session.plugininfo"; 53 54 override void registerResources(IResourceManagerRegistry resmanRegistry) 55 { 56 ConfigManager config = resmanRegistry.getResourceManager!ConfigManager; 57 nicknameOpt = config.registerOption!string("name", "Player"); 58 auto components = resmanRegistry.getResourceManager!EntityComponentRegistry; 59 registerSessionComponents(components.eman); 60 } 61 62 override void init(IPluginManager pluginman) 63 { 64 graphics = pluginman.getPlugin!GraphicsPlugin; 65 66 evDispatcher = pluginman.getPlugin!EventDispatcherPlugin; 67 evDispatcher.subscribeToEvent(&handleThisClientDisconnected); 68 69 clientWorld = pluginman.getPlugin!ClientWorld; 70 71 connection = pluginman.getPlugin!NetClientPlugin; 72 connection.registerPacketHandler!SessionInfoPacket(&handleSessionInfoPacket); 73 connection.registerPacketHandler!ClientLoggedInPacket(&handleUserLoggedInPacket); 74 connection.registerPacketHandler!ClientLoggedOutPacket(&handleUserLoggedOutPacket); 75 connection.registerPacketHandler!DimensionInfoPacket(&handleDimensionInfoPacket); 76 connection.registerPacketHandler!ClientPositionPacket(&handleClientPositionPacket); 77 connection.registerPacketHandler!SpawnPacket(&handleSpawnPacket); 78 connection.registerPacketHandler!GameStartPacket(&handleGameStartPacket); 79 } 80 81 void handleThisClientDisconnected(ref ThisClientDisconnectedEvent event) 82 { 83 isSpawned = false; 84 } 85 86 void handleGameStartPacket(ubyte[] packetData) 87 { 88 connection.send(LoginPacket(nicknameOpt.get!string)); 89 evDispatcher.postEvent(ThisClientConnectedEvent()); 90 evDispatcher.postEvent(SendClientSettingsEvent()); 91 connection.send(GameStartPacket()); 92 } 93 94 void handleUserLoggedInPacket(ubyte[] packetData) 95 { 96 auto newUser = unpackPacket!ClientLoggedInPacket(packetData); 97 clientNames[newUser.clientId] = newUser.clientName; 98 infof("%s has connected", newUser.clientName); 99 evDispatcher.postEvent(ClientLoggedInEvent(newUser.clientId)); 100 } 101 102 void handleUserLoggedOutPacket(ubyte[] packetData) 103 { 104 auto packet = unpackPacket!ClientLoggedOutPacket(packetData); 105 infof("%s has disconnected", clientName(packet.clientId)); 106 evDispatcher.postEvent(ClientLoggedOutEvent(packet.clientId)); 107 clientNames.remove(packet.clientId); 108 } 109 110 void handleSessionInfoPacket(ubyte[] packetData) 111 { 112 auto loginInfo = unpackPacket!SessionInfoPacket(packetData); 113 114 clientNames = loginInfo.clientNames; 115 thisSessionId = loginInfo.yourId; 116 thisEntityId = loginInfo.yourId; 117 evDispatcher.postEvent(ThisClientLoggedInEvent(thisSessionId)); 118 } 119 120 // borders have changed, affects loaded/added chunks 121 void handleDimensionInfoPacket(ubyte[] packetData) 122 { 123 auto packet = unpackPacket!DimensionInfoPacket(packetData); 124 infof("borders %s %s", packet.dimension, packet.borders); 125 clientWorld.setDimensionBorders(packet.dimension, packet.borders); 126 } 127 128 // position has changed, affects loaded/added chunks 129 void handleClientPositionPacket(ubyte[] packetData) 130 { 131 auto packet = unpackPacket!ClientPositionPacket(packetData); 132 //tracef("Received ClientPositionPacket(%s, %s, %s, %s)", 133 // packet.pos, packet.heading, packet.dimension, packet.positionKey); 134 135 nansToZero(packet.dimPos.pos); 136 graphics.camera.position = vec3(packet.dimPos.pos); 137 138 nansToZero(packet.dimPos.heading); 139 graphics.camera.setHeading(vec2(packet.dimPos.heading)); 140 141 clientWorld.setCurrentDimension(packet.dimension, packet.positionKey); 142 } 143 144 void handleSpawnPacket(ubyte[] packetData) 145 { 146 auto packet = unpackPacket!SpawnPacket(packetData); 147 isSpawned = true; 148 clientWorld.updateObserverPosition(); 149 } 150 151 string clientName(EntityId clientId) 152 { 153 return clientId in clientNames ? clientNames[clientId] : format("? %s", clientId); 154 } 155 }