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