1 /**
2 Copyright: Copyright (c) 2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module server;
8 
9 import std.stdio;
10 import std.range;
11 
12 import derelict.enet.enet;
13 
14 import connection;
15 import packets;
16 
17 
18 struct ServerSettings
19 {
20 	ushort port;
21 	size_t maxClients;
22 	size_t numChannels;
23 	uint incomingBandwidth;
24 	uint outgoingBandwidth;
25 }
26 
27 struct User
28 {
29 	string name;
30 	ENetPeer* peer;
31 }
32 
33 struct UserStorage
34 {
35 	private User*[UserId] users;
36 
37 	UserId addUser(ENetPeer* peer)
38 	{
39 		UserId id = nextPeerId;
40 		User* user = new User;
41 		user.peer = peer;
42 		users[id] = user;
43 		return id;
44 	}
45 
46 	void removeUser(UserId id)
47 	{
48 		users.remove(id);
49 	}
50 
51 	User* findUser(UserId id)
52 	{
53 		return users.get(id, null);
54 	}
55 
56 	UserId nextPeerId() @property
57 	{
58 		return _nextPeerId++;
59 	}
60 
61 	string[UserId] userNames()
62 	{
63 		string[UserId] names;
64 		foreach(id, user; users)
65 		{
66 			names[id] = user.name;
67 		}
68 
69 		return names;
70 	}
71 
72 	string userName(UserId id)
73 	{
74 		return users[id].name;
75 	}
76 
77 	ENetPeer* userPeer(UserId id)
78 	{
79 		return users[id].peer;
80 	}
81 
82 	size_t length()
83 	{
84 		return users.length;
85 	}
86 
87 	private UserId _nextPeerId = 1;
88 }
89 
90 class Server : Connection
91 {
92 	bool isRunning;
93 
94 	ENetAddress address;
95 	ServerSettings settings;
96 
97 	UserStorage userStorage;
98 
99 	void start(ServerSettings _settings)
100 	{
101 		side = "Server";
102 
103 		registerPacket!LoginPacket(&handleLoginPacket);
104 		registerPacket!SessionInfoPacket;
105 		registerPacket!UserLoggedInPacket;
106 		registerPacket!UserLoggedOutPacket;
107 		registerPacket!MessagePacket(&handleMessagePacket);
108 
109 		printPacketMap();
110 
111 		settings = _settings;
112 
113 		address.host = ENET_HOST_ANY;
114 		address.port = settings.port;
115 
116 		host = enet_host_create(&address,
117 			settings.maxClients,
118 			settings.numChannels,
119 			settings.incomingBandwidth,
120 			settings.outgoingBandwidth);
121 
122 		if (host is null)
123 		{
124 			writeln("Server: An error occured while trying to create an ENet server host");
125 			return;
126 		}
127 
128 		isRunning = true;
129 	}
130 
131 	void sendMessageTo(UserId userId, string message)
132 	{
133 		sendTo(only(userId), createPacket(MessagePacket(0, message)));
134 	}
135 
136 	void handleCommand(string command, UserId userId)
137 	{
138 		import std.algorithm : splitter;
139 		import std.string : format;
140 		writefln("Server: %s Command> %s", userStorage.userName(userId), command);
141 		
142 		if (command.length <= 1)
143 		{
144 			sendMessageTo(userId, "Invalid command");
145 			return;
146 		}
147 
148 		// Split without leading '/'
149 		auto splitted = command[1..$].splitter;
150 		string commName = splitted.front;
151 		splitted.popFront;
152 
153 		if (commName == "stop")
154 			isRunning = false;
155 		else
156 			sendMessageTo(userId, format("Unknown command %s", commName));
157 	}
158 
159 	override void stop()
160 	{
161 		super.stop();
162 		writefln("Server: Stopped");
163 	}
164 
165 	/// Sending
166 	void sendTo(R)(R users, ubyte[] data, ubyte channel = 0)
167 		if (isInputRange!R && is(ElementType!R == UserId))
168 	{
169 		ENetPacket *packet = enet_packet_create(data.ptr, data.length,
170 				ENET_PACKET_FLAG_RELIABLE);
171 		sendTo(users, packet, channel);
172 	}
173 
174 	/// ditto
175 	void sendTo(R)(R users, ENetPacket* packet, ubyte channel = 0)
176 		if (isInputRange!R && is(ElementType!R == UserId))
177 	{
178 		foreach(userId; users)
179 		{
180 			enet_peer_send(userStorage.userPeer(userId), channel, packet);
181 		}
182 	}
183 
184 	/// ditto
185 	void sendToAll(ubyte[] data, ubyte channel = 0)
186 	{
187 		ENetPacket *packet = enet_packet_create(data.ptr, data.length,
188 					ENET_PACKET_FLAG_RELIABLE);
189 		sendToAll(packet, channel);
190 	}
191 
192 	/// ditto
193 	void sendToAll(ENetPacket* packet, ubyte channel = 0)
194 	{
195 		enet_host_broadcast(host, channel, packet);
196 	}
197 
198 	//-------------------------------------------------------------------------
199 	// Handlers
200 
201 	override void onConnect(ref ENetEvent event)
202 	{
203 		writefln("Server: A new client connected from %(%s.%):%s", 
204 			*cast(ubyte[4]*)(&event.peer.address.host),
205 			event.peer.address.port);
206 
207 		event.peer.data = cast(void*)userStorage.addUser(event.peer);
208 		enet_peer_timeout(event.peer, 0, 0, 2000);
209 	}
210 
211 	void handleLoginPacket(ubyte[] packetData, UserId userId)
212 	{
213 		LoginPacket packet = unpackPacket!LoginPacket(packetData);
214 		
215 		userStorage.findUser(userId).name = packet.userName;
216 		writefln("Server: %s logged in", packet.userName);
217 		
218 		sendTo(only(userId), createPacket(SessionInfoPacket(userId, userStorage.userNames)));
219 		sendToAll(createPacket(UserLoggedInPacket(userId, packet.userName)));
220 	}
221 
222 	void handleMessagePacket(ubyte[] packetData, UserId userId)
223 	{
224 		import std.algorithm : startsWith;
225 		import std.string : strip;
226 
227 		MessagePacket packet = unpackPacket!MessagePacket(packetData);
228 			
229 		packet.userId = userId;
230 		string strippedMsg = packet.msg.strip;
231 		
232 		if (strippedMsg.startsWith("/"))
233 		{
234 			handleCommand(strippedMsg, userId);
235 			return;
236 		}
237 
238 		writefln("Server: %s> %s", userStorage.userName(userId), packet.msg);
239 		
240 		sendToAll(createPacket(packet));
241 	}
242 
243 	override void onDisconnect(ref ENetEvent event)
244 	{
245 		UserId userId = cast(UserId)event.peer.data;
246 		
247 		writefln("Server: %s disconnected", userStorage.userName(userId));
248 		
249 		userStorage.removeUser(userId);
250 
251 		sendToAll(createPacket(UserLoggedOutPacket(userId)));
252 
253 		// Reset client's information
254 		event.peer.data = null;
255 
256 		if (userStorage.length == 0)
257 			isRunning = false;
258 	}
259 }