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 }