1 #ifndef H_NET 2 #define H_NET 3 4 #include "core.h" 5 #include "utils.h" 6 #include "format.h" 7 #include "controller.h" 8 #include "ui.h" 9 10 #define NET_PROTOCOL 1 11 #define NET_PORT 21468 12 13 #define NET_PING_TIMEOUT ( 1000 * 10 ) 14 #define NET_PING_PERIOD ( 1000 * 3 ) 15 #define NET_SYMC_INPUT_PERIOD ( 1000 / 25 ) 16 #define NET_SYMC_STATE_PERIOD ( 1000 / 1000 ) 17 18 namespace Network { 19 20 struct Packet { 21 enum Type { 22 HELLO, INFO, PING, PONG, JOIN, ACCEPT, REJECT, INPUT, STATE, 23 }; 24 25 uint16 type; 26 uint16 id; 27 28 union { 29 struct { 30 uint8 protocol; 31 uint8 game; 32 } hello; 33 34 struct { 35 str16 name; 36 uint8 level; 37 uint8 players; 38 struct { 39 uint16 secure:1; 40 } flags; 41 } info; 42 43 struct { 44 str16 nick; 45 str16 pass; 46 } join; 47 48 struct { 49 uint16 id; 50 uint8 level; 51 uint8 roomIndex; 52 int16 posX; 53 int16 posY; 54 int16 posZ; 55 int16 angle; 56 } accept; 57 58 struct { 59 uint16 reason; 60 } reject; 61 62 struct { 63 uint16 mask; 64 } input; 65 66 struct { 67 uint8 roomIndex; 68 uint8 reserved; 69 int16 pos[3]; 70 int16 angle[2]; 71 uint8 frame; 72 uint8 stand; 73 uint16 animIndex; 74 } state; 75 }; 76 getSizePacket77 int getSize() const { 78 const int sizes[] = { 79 sizeof(hello), 80 sizeof(info), 81 0, 82 0, 83 sizeof(join), 84 sizeof(accept), 85 sizeof(reject), 86 sizeof(input), 87 sizeof(state), 88 }; 89 90 if (type >= 0 && type < COUNT(sizes)) 91 return 2 + 2 + sizes[type]; 92 ASSERT(false); 93 return 0; 94 } 95 }; 96 97 IGame *game; 98 99 struct Player { 100 NAPI::Peer peer; 101 int pingTime; 102 int pingIndex; 103 Controller *controller; 104 }; 105 106 Array<Player> players; 107 108 int syncInputTime; 109 int syncStateTime; 110 start(IGame * game)111 void start(IGame *game) { 112 Network::game = game; 113 NAPI::listen(NET_PORT); 114 syncInputTime = syncStateTime = Core::getTime(); 115 } 116 stop()117 void stop() { 118 players.clear(); 119 } 120 sendPacket(const NAPI::Peer & to,const Packet & packet)121 bool sendPacket(const NAPI::Peer &to, const Packet &packet) { 122 return NAPI::send(to, &packet, packet.getSize()) > 0; 123 } 124 recvPacket(NAPI::Peer & from,Packet & packet)125 bool recvPacket(NAPI::Peer &from, Packet &packet) { 126 int count = NAPI::recv(from, &packet, sizeof(packet)); 127 if (count > 0) { 128 if (count != packet.getSize()) { 129 ASSERT(false); 130 return false; 131 } 132 return true; 133 } 134 return false; 135 } 136 sayHello()137 void sayHello() { 138 Packet packet; 139 packet.type = Packet::HELLO; 140 packet.hello.protocol = NET_PROTOCOL; 141 packet.hello.game = game->getLevel()->version & TR::VER_VERSION; 142 143 NAPI::broadcast(&packet, packet.getSize()); 144 } 145 joinGame(const NAPI::Peer & peer)146 void joinGame(const NAPI::Peer &peer) { 147 Packet packet; 148 packet.type = Packet::JOIN; 149 packet.join.nick = "Player_2"; 150 packet.join.pass = ""; 151 LOG("join game\n"); 152 sendPacket(peer, packet); 153 } 154 pingPlayers(int time)155 void pingPlayers(int time) { 156 int i = 0; 157 while (i < players.length) { 158 int delta = time - players[i].pingTime; 159 160 if (delta > NET_PING_TIMEOUT) { 161 players.removeFast(i); 162 continue; 163 } 164 165 if (delta > NET_PING_PERIOD) { 166 Packet packet; 167 packet.type = Packet::PING; 168 sendPacket(players[i].peer, packet); 169 } 170 171 i++; 172 } 173 } 174 syncInput(int time)175 void syncInput(int time) { 176 Lara *lara = (Lara*)game->getLara(); 177 if (!lara) return; 178 179 if ((time - syncInputTime) < NET_SYMC_INPUT_PERIOD) 180 return; 181 182 Packet packet; 183 packet.type = Packet::INPUT; 184 packet.input.mask = lara->getInput(); 185 186 for (int i = 0; i < players.length; i++) 187 sendPacket(players[i].peer, packet); 188 189 syncInputTime = time; 190 } 191 syncState(int time)192 void syncState(int time) { 193 if ((time - syncStateTime) < NET_SYMC_STATE_PERIOD) 194 return; 195 // TODO 196 syncStateTime = time; 197 } 198 getPlayerByPeer(const NAPI::Peer & peer)199 Player* getPlayerByPeer(const NAPI::Peer &peer) { 200 for (int i = 0; i < players.length; i++) 201 if (players[i].peer == peer) { 202 return &players[i]; 203 } 204 return NULL; 205 } 206 getSpawnPoint(uint8 & roomIndex,vec3 & pos,float & angle)207 void getSpawnPoint(uint8 &roomIndex, vec3 &pos, float &angle) { 208 Controller *lara = game->getLara(); 209 roomIndex = lara->getRoomIndex(); 210 pos = lara->getPos(); 211 angle = normalizeAngle(lara->angle.y); // 0..2PI 212 } 213 update()214 void update() { 215 int count; 216 NAPI::Peer from; 217 Packet packet, response; 218 219 int time = Core::getTime(); 220 221 while ( (count = recvPacket(from, packet)) > 0 ) { 222 Player *player = getPlayerByPeer(from); 223 if (player) 224 player->pingTime = time; 225 226 switch (packet.type) { 227 case Packet::HELLO : 228 if (game->getLevel()->isTitle()) 229 break; 230 231 LOG("recv HELLO\n"); 232 if (packet.hello.game != (game->getLevel()->version & TR::VER_VERSION)) 233 break; 234 if (packet.hello.protocol != NET_PROTOCOL) 235 break; 236 LOG("send INFO\n"); 237 response.type = Packet::INFO; 238 response.info.name = "MultiOpenLara"; 239 response.info.level = game->getLevel()->id; 240 response.info.players = players.length + 1; 241 response.info.flags.secure = false; 242 243 sendPacket(from, response); 244 245 break; 246 247 case Packet::INFO : { 248 LOG("recv INFO\n"); 249 char buf[sizeof(packet.info.name) + 1]; 250 packet.info.name.get(buf); 251 LOG("name: %s\n", buf); 252 joinGame(from); 253 break; 254 } 255 256 case Packet::PING : 257 if (player) { 258 response.type = Packet::PONG; 259 sendPacket(from, response); 260 } 261 break; 262 263 case Packet::PONG : 264 break; 265 266 case Packet::JOIN : 267 if (!player) { 268 uint8 roomIndex; 269 vec3 pos; 270 float angle; 271 272 getSpawnPoint(roomIndex, pos, angle); 273 274 Player newPlayer; 275 newPlayer.peer = from; 276 newPlayer.pingIndex = 0; 277 newPlayer.pingTime = time; 278 newPlayer.controller = game->addEntity(TR::Entity::LARA, roomIndex, pos, angle); 279 players.push(newPlayer); 280 281 ((Lara*)newPlayer.controller)->networkInput = 0; 282 283 char buf[32]; 284 packet.join.nick.get(buf); 285 LOG("Player %s joined\n", buf); 286 287 ASSERT(newPlayer.controller); 288 289 TR::Room &room = game->getLevel()->rooms[roomIndex]; 290 vec3 offset = pos - room.getOffset(); 291 292 response.type = Packet::ACCEPT; 293 response.accept.id = 0; 294 response.accept.level = game->getLevel()->id; 295 response.accept.roomIndex = roomIndex; 296 response.accept.posX = int16(offset.x); 297 response.accept.posY = int16(offset.y); 298 response.accept.posZ = int16(offset.z); 299 response.accept.angle = int16(angle * RAD2DEG); 300 301 sendPacket(from, response); 302 } 303 break; 304 305 case Packet::ACCEPT : { 306 LOG("accept!\n"); 307 game->loadLevel(TR::LevelID(packet.accept.level)); 308 inventory->toggle(); 309 break; 310 } 311 312 case Packet::REJECT : 313 break; 314 315 case Packet::INPUT : 316 if (game->getLevel()->isTitle()) 317 break; 318 319 if (!player) { 320 uint8 roomIndex; 321 vec3 pos; 322 float angle; 323 324 getSpawnPoint(roomIndex, pos, angle); 325 326 Player newPlayer; 327 newPlayer.peer = from; 328 newPlayer.pingIndex = 0; 329 newPlayer.pingTime = time; 330 newPlayer.controller = game->addEntity(TR::Entity::LARA, roomIndex, pos, angle); 331 players.push(newPlayer); 332 333 ((Lara*)newPlayer.controller)->networkInput = 0; 334 335 player = getPlayerByPeer(from); 336 } 337 338 if (player) { 339 ((Lara*)player->controller)->networkInput = packet.input.mask; 340 } 341 break; 342 343 case Packet::STATE : 344 break; 345 } 346 } 347 348 pingPlayers(time); 349 syncInput(time); 350 syncState(time); 351 } 352 } 353 354 #endif 355