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 }