DEADSOFTWARE

08fd9df69598dd87840cabd9fc700ec104c4b37b
[d2df-sdl.git] / src / game / g_netmsg.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_netmsg;
18 interface
20 uses e_msg, g_net, g_triggers, Classes, SysUtils, md5;
22 const
23 NET_MSG_INFO = 100;
25 NET_MSG_CHAT = 101;
26 NET_MSG_SND = 102;
27 NET_MSG_GFX = 103;
28 NET_MSG_GEVENT = 104;
29 NET_MSG_SCORE = 105;
30 NET_MSG_COOP = 106;
31 NET_MSG_FLAG = 107;
32 NET_MSG_REQFST = 108;
33 NET_MSG_GSET = 109;
34 NET_MSG_FLAGPOS= 110;
36 NET_MSG_PLR = 111;
37 NET_MSG_PLRPOS = 112;
38 NET_MSG_PLRSTA = 113;
39 NET_MSG_PLRDEL = 114;
40 NET_MSG_PLRDMG = 115;
41 NET_MSG_PLRDIE = 116;
42 NET_MSG_PLRFIRE= 117;
43 NET_MSG_PLRSET = 119;
44 NET_MSG_CHEAT = 120;
46 NET_MSG_ISPAWN = 121;
47 NET_MSG_IDEL = 122;
48 NET_MSG_IPOS = 123;
50 NET_MSG_MSPAWN = 131;
51 NET_MSG_MPOS = 132;
52 NET_MSG_MSTATE = 133;
53 NET_MSG_MSHOT = 134;
54 NET_MSG_MDEL = 135;
56 NET_MSG_PSTATE = 141;
57 NET_MSG_PTEX = 142;
59 NET_MSG_TSOUND = 151;
60 NET_MSG_TMUSIC = 152;
62 NET_MSG_SHDEL = 161;
63 NET_MSG_SHADD = 162;
64 NET_MSG_SHPOS = 163;
66 NET_MSG_RCON_AUTH = 191;
67 NET_MSG_RCON_CMD = 192;
68 NET_MSG_TIME_SYNC = 194;
69 NET_MSG_VOTE_EVENT = 195;
71 {
72 NET_MSG_MAP_REQUEST = 201;
73 NET_MSG_MAP_RESPONSE = 202;
74 NET_MSG_RES_REQUEST = 203;
75 NET_MSG_RES_RESPONSE = 204;
76 }
78 NET_CHAT_SYSTEM = 0;
79 NET_CHAT_PLAYER = 1;
80 NET_CHAT_TEAM = 2;
82 NET_RCON_NOAUTH = 0;
83 NET_RCON_PWGOOD = 1;
84 NET_RCON_PWBAD = 2;
86 NET_GFX_SPARK = 1;
87 NET_GFX_TELE = 2;
88 NET_GFX_RESPAWN = 3;
89 NET_GFX_FIRE = 4;
90 NET_GFX_EXPLODE = 5;
91 NET_GFX_BFGEXPL = 6;
92 NET_GFX_BFGHIT = 7;
93 NET_GFX_SHELL1 = 8;
94 NET_GFX_SHELL2 = 9;
95 NET_GFX_SHELL3 = 10;
97 NET_EV_MAPSTART = 1;
98 NET_EV_MAPEND = 2;
99 NET_EV_CHANGE_TEAM = 3;
100 NET_EV_PLAYER_KICK = 4;
101 NET_EV_PLAYER_BAN = 5;
102 NET_EV_LMS_WARMUP = 6;
103 NET_EV_LMS_SURVIVOR = 7;
104 NET_EV_RCON = 8;
105 NET_EV_BIGTEXT = 9;
106 NET_EV_SCORE = 10;
107 NET_EV_SCORE_MSG = 11;
108 NET_EV_LMS_START = 12;
109 NET_EV_LMS_WIN = 13;
110 NET_EV_TLMS_WIN = 14;
111 NET_EV_LMS_LOSE = 15;
112 NET_EV_LMS_DRAW = 16;
113 NET_EV_KILLCOMBO = 17;
114 NET_EV_PLAYER_TOUCH = 18;
115 NET_EV_SECRET = 19;
116 NET_EV_INTER_READY = 20;
117 NET_EV_LMS_NOSPAWN = 21;
119 NET_VE_STARTED = 1;
120 NET_VE_PASSED = 2;
121 NET_VE_FAILED = 3;
122 NET_VE_VOTE = 4;
123 NET_VE_REVOKE = 5;
124 NET_VE_INPROGRESS = 6;
126 NET_FLAG_GET = 1;
127 NET_FLAG_DROP = 2;
128 NET_FLAG_CAP = 3;
129 NET_FLAG_RETURN = 4;
131 NET_CHEAT_SUICIDE = 1;
132 NET_CHEAT_SPECTATE = 2;
133 NET_CHEAT_READY = 3;
134 NET_CHEAT_DROPFLAG = 4;
136 NET_MAX_DIFFTIME = 5000 div 36;
138 // HOST MESSAGES
140 procedure MH_MalformedPacket(C: pTNetClient);
141 procedure MH_ProcessFirstSpawn (C: pTNetClient);
143 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
144 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
145 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
146 function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
147 procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg);
148 procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
149 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
150 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
151 //procedure MH_RECV_MapRequest(C: pTNetClient; var M: TMsg);
152 //procedure MH_RECV_ResRequest(C: pTNetClient; var M: TMsg);
153 procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg);
155 // GAME
156 procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NET_EVERYONE});
157 procedure MH_SEND_Info(ID: Byte);
158 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
159 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
160 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
161 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
162 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
163 procedure MH_SEND_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
164 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
165 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
166 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
167 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
168 procedure MH_SEND_FlagPos(Flag: Byte; ID: Integer = NET_EVERYONE);
169 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
170 // PLAYER
171 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
172 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
173 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
174 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
175 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
176 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
177 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
178 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
179 // ITEM
180 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
181 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
182 procedure MH_SEND_ItemPos(IID: Word; ID: Integer = NET_EVERYONE);
183 // PANEL
184 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
185 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
186 // MONSTER
187 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
188 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
189 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
190 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
191 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
192 // TRIGGER
193 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
194 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
195 // MISC
196 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
197 procedure MH_SEND_VoteEvent(EvType: Byte;
198 StrArg1: string = 'a'; StrArg2: string = 'b';
199 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
200 ID: Integer = NET_EVERYONE);
202 // CLIENT MESSAGES //
204 // GAME
205 procedure MC_RECV_Chat(var M: TMsg);
206 procedure MC_RECV_Effect(var M: TMsg);
207 procedure MC_RECV_Sound(var M: TMsg);
208 procedure MC_RECV_GameStats(var M: TMsg);
209 procedure MC_RECV_CoopStats(var M: TMsg);
210 procedure MC_RECV_GameEvent(var M: TMsg);
211 procedure MC_RECV_FlagEvent(var M: TMsg);
212 procedure MC_RECV_FlagPos(var M: TMsg);
213 procedure MC_RECV_GameSettings(var M: TMsg);
214 // PLAYER
215 function MC_RECV_PlayerCreate(var M: TMsg): Word;
216 function MC_RECV_PlayerPos(var M: TMsg): Word;
217 function MC_RECV_PlayerStats(var M: TMsg): Word;
218 function MC_RECV_PlayerDelete(var M: TMsg): Word;
219 function MC_RECV_PlayerDamage(var M: TMsg): Word;
220 function MC_RECV_PlayerDeath(var M: TMsg): Word;
221 function MC_RECV_PlayerFire(var M: TMsg): Word;
222 procedure MC_RECV_PlayerSettings(var M: TMsg);
223 // ITEM
224 procedure MC_RECV_ItemSpawn(var M: TMsg);
225 procedure MC_RECV_ItemDestroy(var M: TMsg);
226 procedure MC_RECV_ItemPos(var M: TMsg);
227 // PANEL
228 procedure MC_RECV_PanelTexture(var M: TMsg);
229 procedure MC_RECV_PanelState(var M: TMsg);
230 // MONSTER
231 procedure MC_RECV_MonsterSpawn(var M: TMsg);
232 procedure MC_RECV_MonsterPos(var M: TMsg);
233 procedure MC_RECV_MonsterState(var M: TMsg);
234 procedure MC_RECV_MonsterShot(var M: TMsg);
235 procedure MC_RECV_MonsterDelete(var M: TMsg);
236 // SHOT
237 procedure MC_RECV_CreateShot(var M: TMsg);
238 procedure MC_RECV_UpdateShot(var M: TMsg);
239 procedure MC_RECV_DeleteShot(var M: TMsg);
240 // TRIGGER
241 procedure MC_RECV_TriggerSound(var M: TMsg);
242 procedure MC_RECV_TriggerMusic(var M: TMsg);
243 // MISC
244 procedure MC_RECV_TimeSync(var M: TMsg);
245 procedure MC_RECV_VoteEvent(var M: TMsg);
246 // SERVICE
247 procedure MC_SEND_Info(Password: string);
248 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
249 procedure MC_SEND_PlayerPos();
250 procedure MC_SEND_FullStateRequest();
251 procedure MC_SEND_PlayerSettings();
252 procedure MC_SEND_CheatRequest(Kind: Byte);
253 procedure MC_SEND_RCONPassword(Password: string);
254 procedure MC_SEND_RCONCommand(Cmd: string);
255 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
256 // DOWNLOAD
257 //procedure MC_SEND_MapRequest();
258 //procedure MC_SEND_ResRequest(const resName: AnsiString);
261 type
262 TExternalResourceInfo = record
263 Name: string[255];
264 md5: TMD5Digest;
265 end;
267 TResDataMsg = record
268 MsgId: Byte;
269 FileSize: Integer;
270 FileData: AByte;
271 end;
273 TMapDataMsg = record
274 MsgId: Byte;
275 FileSize: Integer;
276 FileData: AByte;
277 ExternalResources: array of TExternalResourceInfo;
278 end;
280 function IsValidFileName(const S: String): Boolean;
281 function IsValidFilePath(const S: String): Boolean;
284 implementation
286 uses
287 {$IFDEF ENABLE_MENU}
288 g_gui,
289 {$ENDIF}
290 {$IFDEF ENABLE_GFX}
291 g_gfx,
292 {$ENDIF}
293 {$IFDEF ENABLE_GIBS}
294 g_gibs,
295 {$ENDIF}
296 {$IFDEF ENABLE_SHELLS}
297 g_shells,
298 {$ENDIF}
299 {$IFDEF ENABLE_CORPSES}
300 g_corpses,
301 {$ENDIF}
302 Math, ENet, e_input, e_log, g_base, g_basic,
303 g_sound, g_console, g_options,
304 g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys,
305 g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF
308 const
309 NET_KEY_LEFT = 1 shl 0;
310 NET_KEY_RIGHT = 1 shl 1;
311 NET_KEY_UP = 1 shl 2;
312 NET_KEY_DOWN = 1 shl 3;
313 NET_KEY_JUMP = 1 shl 4;
314 NET_KEY_FIRE = 1 shl 5;
315 NET_KEY_OPEN = 1 shl 6;
316 NET_KEY_CHAT = 1 shl 7;
317 NET_KEY_FORCEDIR = 1 shl 8;
319 //var
320 //kBytePrev: Word = 0;
321 //kDirPrev: TDirection = D_LEFT;
322 //HostGameTime: Word = 0;
325 function IsValidFileName(const S: String): Boolean;
326 const
327 Forbidden: set of Char = ['<', '>', '|', '"', ':', '*', '?'];
328 var
329 I: Integer;
330 begin
331 Result := S <> '';
332 for I := 1 to Length(S) do
333 Result := Result and (not(S[I] in Forbidden));
334 end;
336 function IsValidFilePath(const S: String): Boolean;
337 var
338 I: Integer;
339 begin
340 Result := False;
341 if not IsValidFileName(S) then exit;
342 if FileExists(S) then exit;
343 I := LastDelimiter('\/', S);
344 if (I > 0) then
345 if (not DirectoryExists(Copy(S, 1, I-1))) then
346 exit;
347 Result := True;
348 end;
351 // HOST MESSAGES //
354 // GAME
356 procedure MH_MalformedPacket(C: pTNetClient);
357 begin
358 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
359 _lc[I_NET_DISC_PROTOCOL]);
360 g_Net_Host_Kick(C^.ID, NET_DISC_PROTOCOL);
361 end;
363 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
364 var
365 Txt: string;
366 Mode: Byte;
367 PID: Word;
368 Pl: TPlayer;
369 Err: Boolean;
370 begin
371 PID := C^.Player;
372 Pl := g_Player_Get(PID);
374 Err := False;
375 try
376 Txt := M.ReadString();
377 Mode := M.ReadByte();
378 except
379 Err := True;
380 end;
382 if Err then begin MH_MalformedPacket(C); Exit; end;
384 if (Mode = NET_CHAT_SYSTEM) then
385 Mode := NET_CHAT_PLAYER; // prevent sending system messages from clients
386 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
387 Mode := NET_CHAT_PLAYER; // revert to player chat in non-team game
389 if Pl = nil then
390 MH_SEND_Chat(Txt, Mode)
391 else
392 MH_SEND_Chat(Pl.Name + ': ' + Txt, Mode, IfThen(Mode = NET_CHAT_TEAM, Pl.Team, NET_EVERYONE));
393 end;
395 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
396 var
397 Ver, PName, Model, Pw: string;
398 R, G, B, T: Byte;
399 WeapSwitch: Byte;
400 TmpPrefArray: Array [WP_FIRST .. WP_LAST + 1] of Byte;
401 SwitchEmpty: Byte;
402 SkipF: Byte;
403 PID: Word;
404 Color: TRGB;
405 I: Integer;
406 Err: Boolean;
407 begin
408 Err := False;
409 try
410 Ver := M.ReadString();
411 Pw := M.ReadString();
412 PName := M.ReadString();
413 Model := M.ReadString();
414 R := M.ReadByte();
415 G := M.ReadByte();
416 B := M.ReadByte();
417 T := M.ReadByte();
418 WeapSwitch := M.ReadByte();
419 for I := WP_FIRST to WP_LAST + 1 do
420 TmpPrefArray[I] := M.ReadByte();
421 SwitchEmpty := M.ReadByte();
422 SkipF := M.ReadByte();
423 except
424 Err := True;
425 end;
427 if Err then begin MH_MalformedPacket(C); Exit; end;
429 if Ver <> GAME_VERSION then
430 begin
431 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
432 _lc[I_NET_DISC_VERSION]);
433 g_Net_Host_Kick(C^.ID, NET_DISC_VERSION);
434 Exit;
435 end;
437 if g_Net_IsHostBanned(C^.Peer^.address.host) then
438 begin
439 if g_Net_IsHostBanned(C^.Peer^.address.host, True) then
440 begin
441 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
442 _lc[I_NET_DISC_BAN]);
443 g_Net_Host_Kick(C^.ID, NET_DISC_BAN);
444 end
445 else
446 begin
447 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
448 _lc[I_NET_DISC_BAN]);
449 g_Net_Host_Kick(C^.ID, NET_DISC_TEMPBAN);
450 end;
451 Exit;
452 end;
454 if NetPassword <> '' then
455 if AnsiLowerCase(NetPassword) <> AnsiLowerCase(Pw) then
456 begin
457 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
458 _lc[I_NET_DISC_PASSWORD]);
459 g_Net_Host_Kick(C^.ID, NET_DISC_PASSWORD);
460 Exit;
461 end;
463 if (C^.Player <> 0) then
464 begin
465 // already received info
466 g_Net_Penalize(C, 'client info spam');
467 Exit;
468 end;
470 Color.R := R;
471 Color.B := B;
472 Color.G := G;
474 PID := g_Player_Create(Model, Color, T, False);
475 with g_Player_Get(PID) do
476 begin
477 Name := PName;
478 WeapSwitchMode := WeapSwitch;
479 SetWeaponPrefs(TmpPrefArray);
480 SwitchToEmpty := SwitchEmpty;
481 SkipFist := SkipF;
482 if (g_Force_Model_Get() <> 0) then
483 SetModel(g_Forced_Model_GetName());
484 Reset(True);
485 end;
487 C^.Player := PID;
488 C^.WaitForFirstSpawn := false;
489 C^.AuthTime := 0;
491 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
492 e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) +
493 '] connected. Assigned player #' + IntToStr(PID) + '.', TMsgType.Notify);
495 MH_SEND_Info(C^.ID);
497 with g_Player_Get(PID) do
498 begin
499 Name := PName;
500 FClientID := C^.ID;
501 // round in progress, don't spawn
502 e_LogWritefln('*** client #%u (cid #%u) authenticated...', [C.ID, C.Player]);
503 //e_LogWritefln('spawning player with pid #%u...', [PID]);
504 //Respawn(gGameSettings.GameType = GT_SINGLE);
505 //k8: no, do not spawn a player yet, wait for "request full state" packet
506 Lives := 0;
507 Spectate;
508 FNoRespawn := True;
509 // `FWantsInGame` seems to mean "spawn the player on the next occasion".
510 // that is, if we'll set it to `true`, the player can be spawned after
511 // warmup time ran out, for example, regardless of the real player state.
512 // also, this seems to work only for the initial connection. further
513 // map changes could initiate resource downloading, but the player will
514 // be spawned immediately.
515 // the proper solution will require another player state, "ephemeral".
516 // the player should start any map in "ephemeral" state, and turned into
517 // real mobj only when they sent a special "i am ready" packet. this packet
518 // must be sent after receiving the full state, so the player will get a full
519 // map view before going into game.
520 FWantsInGame := false;
521 C^.WaitForFirstSpawn := true;
522 end;
524 //if not C^.WaitForFirstSpawn then
525 begin
526 for I := Low(NetClients) to High(NetClients) do
527 begin
528 if NetClients[I].ID = C^.ID then Continue;
529 MH_SEND_PlayerCreate(PID, NetClients[I].ID);
530 MH_SEND_PlayerPos(True, PID, NetClients[I].ID);
531 MH_SEND_PlayerStats(PID, NetClients[I].ID);
532 end;
533 end;
535 if gState in [STATE_INTERCUSTOM, STATE_FOLD] then
536 MH_SEND_GameEvent(NET_EV_MAPEND, 0, 'N', C^.ID);
538 if NetUseMaster then
539 begin
540 //g_Net_Slist_Update;
541 g_Net_Slist_Pulse();
542 end;
543 end;
546 procedure MH_ProcessFirstSpawn (C: pTNetClient);
547 var
548 plr: TPlayer;
549 begin
550 if not C.WaitForFirstSpawn then exit;
551 plr := g_Player_Get(C^.Player);
552 if not assigned(plr) then exit;
553 g_Net_Slist_ServerPlayerComes();
554 e_LogWritefln('*** client #%u (cid #%u) first spawn', [C.ID, C.Player]);
555 C.WaitForFirstSpawn := false;
556 plr.FNoRespawn := false;
557 plr.FWantsInGame := true; // TODO: look into this later
559 if (gGameSettings.MaxLives > 0) and (gLMSRespawn = LMS_RESPAWN_NONE) then
560 begin
561 plr.Spectate;
562 MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, 0, 'N', C.ID);
563 end
564 else
565 begin
566 plr.Respawn(False);
567 if gLMSRespawn > LMS_RESPAWN_NONE then
568 MH_SEND_GameEvent(NET_EV_LMS_WARMUP, gLMSRespawnTime - gTime, 'N', C.ID);
569 end;
570 end;
573 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
574 begin
575 //e_LogWritefln('*** client #%u (cid #%u) full state request', [C.ID, C.Player]);
577 if C^.FullUpdateSent then
578 begin
579 // FullStateRequest spam?
580 g_Net_Penalize(C, 'duplicate full state request');
581 exit;
582 end;
584 if gGameOn then
585 begin
586 MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
587 end
588 else
589 begin
590 C^.RequestedFullUpdate := True;
591 end;
592 end;
594 // PLAYER
596 function MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
597 var
598 Dir, i: Byte;
599 WeaponAct: Byte;
600 WeaponSelect: Word;
601 PID: Word;
602 kByte: Word;
603 Pl: TPlayer;
604 GT: LongWord;
605 Err: Boolean;
606 begin
607 Result := 0;
608 Err := False;
609 if not gGameOn then Exit;
611 try
612 GT := M.ReadLongWord();
613 except
614 Err := True;
615 end;
617 if Err then begin MH_MalformedPacket(C); Exit; end;
619 PID := C^.Player;
620 Pl := g_Player_Get(PID);
621 if Pl = nil then
622 Exit;
624 if (GT > gTime + NET_MAX_DIFFTIME) or (GT < Pl.NetTime) then Exit;
626 with Pl do
627 begin
628 NetTime := GT;
629 try
630 kByte := M.ReadWord();
631 Dir := M.ReadByte();
632 WeaponAct := M.ReadByte();
633 WeaponSelect := M.ReadWord();
634 except
635 Err := True;
636 end;
638 if Err then begin MH_MalformedPacket(C); Exit; end;
640 //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING);
641 if Direction <> TDirection(Dir) then
642 JustTeleported := False;
644 SetDirection(TDirection(Dir));
645 ReleaseKeys;
647 if kByte = NET_KEY_CHAT then
648 begin
649 PressKey(KEY_CHAT, 10000);
650 Exit;
651 end;
653 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
654 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
655 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
656 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
657 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
658 if LongBool(kByte and NET_KEY_FIRE) then PressKey(KEY_FIRE, 10000);
659 if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000);
661 for i := 0 to 7 do
662 begin
663 if (WeaponAct and Byte(1 shl i)) <> 0 then
664 begin
665 //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
666 ProcessWeaponAction(i);
667 end;
668 end;
670 for i := 0 to 15 do
671 begin
672 if (WeaponSelect and Word(1 shl i)) <> 0 then
673 begin
674 //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
675 QueueWeaponSwitch(i);
676 end;
677 end;
678 end;
680 // MH_SEND_PlayerPos(False, PID, C^.ID);
681 end;
683 procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
684 var
685 CheatKind: Byte;
686 Pl: TPlayer;
687 Err: Boolean;
688 begin
689 Err := False;
690 Pl := g_Player_Get(C^.Player);
691 if Pl = nil then Exit;
693 try
694 CheatKind := M.ReadByte();
695 except
696 Err := True;
697 end;
699 if Err then begin MH_MalformedPacket(C); Exit; end;
701 case CheatKind of
702 NET_CHEAT_SUICIDE:
703 Pl.Damage(SUICIDE_DAMAGE, Pl.UID, 0, 0, HIT_SELF);
704 NET_CHEAT_SPECTATE:
705 begin
706 if Pl.FSpectator then
707 begin
708 if (gGameSettings.MaxLives = 0) or (gLMSRespawn > LMS_RESPAWN_NONE) then
709 Pl.Respawn(False)
710 else
711 MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, Pl.UID);
712 end
713 else
714 Pl.Spectate;
715 end;
716 NET_CHEAT_READY:
717 begin
718 if gState <> STATE_INTERCUSTOM then Exit;
719 Pl.FReady := not Pl.FReady;
720 if Pl.FReady then
721 begin
722 MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'Y');
723 Inc(gInterReadyCount);
724 end
725 else
726 begin
727 MH_SEND_GameEvent(NET_EV_INTER_READY, Pl.UID, 'N');
728 Dec(gInterReadyCount);
729 end;
730 end;
731 NET_CHEAT_DROPFLAG:
732 Pl.TryDropFlag();
733 end;
734 end;
736 procedure MH_RECV_PlayerSettings(C: pTNetClient; var M: TMsg);
737 var
738 TmpName: string;
739 TmpModel: string;
740 TmpColor: TRGB;
741 TmpTeam: Byte;
742 TmpWeapSwitch: Byte;
743 TmpPrefArray: Array [WP_FIRST .. WP_LAST + 1] of Byte;
744 TmpSwEmpty: Byte;
745 TmpSkipF: Byte;
746 I: Integer;
747 Pl: TPlayer;
748 Err: Boolean;
749 begin
750 Err := False;
751 try
752 TmpName := M.ReadString();
753 TmpModel := M.ReadString();
754 TmpColor.R := M.ReadByte();
755 TmpColor.G := M.ReadByte();
756 TmpColor.B := M.ReadByte();
757 TmpTeam := M.ReadByte();
758 TmpWeapSwitch := M.ReadByte();
759 for I := WP_FIRST to WP_LAST + 1 do
760 TmpPrefArray[I] := M.ReadByte();
761 TmpSwEmpty := M.ReadByte();
762 TmpSkipF := M.ReadByte();
763 except
764 Err := True;
765 end;
767 if Err then begin MH_MalformedPacket(C); Exit; end;
769 Pl := g_Player_Get(C^.Player);
770 if Pl = nil then Exit;
772 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
773 Pl.SwitchTeam
774 else
775 Pl.SetColor(TmpColor);
777 if Pl.Name <> TmpName then
778 begin
779 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
780 Pl.Name := TmpName;
781 end;
783 if (g_Force_Model_Get() <> 0) then
784 TmpModel := g_Forced_Model_GetName();
785 if TmpModel <> Pl.Model.GetName() then
786 Pl.SetModel(TmpModel);
788 if (TmpWeapSwitch <> Pl.WeapSwitchMode) then
789 Pl.WeapSwitchMode := TmpWeapSwitch;
791 Pl.SetWeaponPrefs(TmpPrefArray);
792 if (TmpSwEmpty <> Pl.SwitchToEmpty) then
793 Pl.SwitchToEmpty := TmpSwEmpty;
795 if (TmpSkipF <> Pl.SkipFist) then
796 Pl.SkipFist := TmpSkipF;
798 MH_SEND_PlayerSettings(Pl.UID, TmpModel);
799 end;
801 // RCON
803 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
804 var
805 Pwd: string;
806 Err: Boolean;
807 begin
808 Err := False;
809 try
810 Pwd := M.ReadString();
811 except
812 Err := True;
813 end;
814 if Err then begin MH_MalformedPacket(C); Exit; end;
815 if not NetAllowRCON then Exit;
816 if Pwd = NetRCONPassword then
817 begin
818 C^.RCONAuth := True;
819 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWGOOD, 'N', C^.ID);
820 end
821 else
822 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_PWBAD, 'N', C^.ID);
823 end;
825 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
826 var
827 Cmd: string;
828 Err: Boolean;
829 begin
830 Err := False;
831 try
832 Cmd := M.ReadString();
833 except
834 Err := True;
835 end;
836 if Err then begin MH_MalformedPacket(C); Exit; end;
837 if not NetAllowRCON then Exit;
838 if not C^.RCONAuth then
839 begin
840 MH_SEND_GameEvent(NET_EV_RCON, NET_RCON_NOAUTH, 'N', C^.ID);
841 Exit;
842 end;
843 g_Console_Process(Cmd);
844 end;
846 // MISC
848 procedure MH_RECV_Vote(C: pTNetClient; var M: TMsg);
849 var
850 Start: Boolean;
851 Name, Command: string;
852 Need: Integer;
853 Pl: TPlayer;
854 Err: Boolean;
855 begin
856 Err := False;
857 try
858 Start := M.ReadByte() <> 0;
859 Command := M.ReadString();
860 except
861 Err := True;
862 end;
864 if Err then begin MH_MalformedPacket(C); Exit; end;
866 Pl := g_Player_Get(C^.Player);
867 if Pl = nil then Exit;
868 Name := Pl.Name;
870 if Start then
871 begin
872 if not g_Console_CommandBlacklisted(Command) then
873 g_Game_StartVote(Command, Name);
874 end
875 else if gVoteInProgress then
876 begin
877 if (gPlayer1 <> nil) or (gPlayer2 <> nil) then
878 Need := Floor((NetClientCount+1)/2.0) + 1
879 else
880 Need := Floor(NetClientCount/2.0) + 1;
881 if C^.Voted then
882 begin
883 Dec(gVoteCount);
884 C^.Voted := False;
885 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_REVOKED], [Name, gVoteCount, Need]), True);
886 MH_SEND_VoteEvent(NET_VE_REVOKE, Name, 'a', gVoteCount, Need);
887 end
888 else
889 begin
890 Inc(gVoteCount);
891 C^.Voted := True;
892 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Name, gVoteCount, Need]), True);
893 MH_SEND_VoteEvent(NET_VE_VOTE, Name, 'a', gVoteCount, Need);
894 g_Game_CheckVote;
895 end;
896 end;
897 end;
899 // GAME (SEND)
901 procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NET_EVERYONE});
903 function sendItemRespawn (it: PItem): Boolean;
904 begin
905 result := false; // don't stop
906 MH_SEND_ItemSpawn(True, it.myid, ID);
907 end;
909 function sendMonSpawn (mon: TMonster): Boolean;
910 begin
911 result := false; // don't stop
912 MH_SEND_MonsterSpawn(mon.UID, ID);
913 end;
915 function sendPanelState (pan: TPanel): Boolean;
916 begin
917 result := false; // don't stop
918 MH_SEND_PanelState(pan.guid, ID); // anyway, to sync mplats
919 if (pan.CanChangeTexture) then MH_SEND_PanelTexture(pan.guid, pan.LastAnimLoop, ID);
920 end;
922 var
923 I: Integer;
924 begin
925 if (ID < 0) or (ID >= Length(NetClients)) then
926 exit; // bogus client, this shouldn't happen
928 NetClients[ID].FullUpdateSent := True;
930 e_LogWritefln('*** client #%u (cid #%u) will get everything', [ID, NetClients[ID].Player]);
932 MH_ProcessFirstSpawn(@NetClients[ID]);
934 if gPlayers <> nil then
935 begin
936 for I := Low(gPlayers) to High(gPlayers) do
937 begin
938 if gPlayers[I] <> nil then
939 begin
940 if CreatePlayers then MH_SEND_PlayerCreate(gPlayers[I].UID, ID);
941 MH_SEND_PlayerPos(True, gPlayers[I].UID, ID);
942 MH_SEND_PlayerStats(gPlayers[I].UID, ID);
944 if (gPlayers[I].Flag <> FLAG_NONE) and (gGameSettings.GameMode = GM_CTF) then
945 begin
946 MH_SEND_FlagEvent(FLAG_STATE_CAPTURED, gPlayers[I].Flag, gPlayers[I].UID, True, ID);
947 end;
948 end;
949 end;
950 end;
952 g_Items_ForEachAlive(sendItemRespawn, true); // backwards
953 g_Mons_ForEach(sendMonSpawn);
954 g_Map_ForEachPanel(sendPanelState);
956 if gTriggers <> nil then
957 begin
958 for I := Low(gTriggers) to High(gTriggers) do
959 begin
960 if gTriggers[I].TriggerType = TRIGGER_SOUND then
961 begin
962 MH_SEND_TriggerSound(gTriggers[I], ID);
963 end;
964 end;
965 end;
967 if Shots <> nil then
968 begin
969 for I := Low(Shots) to High(Shots) do
970 begin
971 if Shots[i].ShotType in [6, 7, 8] then
972 begin
973 MH_SEND_CreateShot(i, ID);
974 end;
975 end;
976 end;
978 MH_SEND_TriggerMusic(ID);
980 MH_SEND_GameStats(ID);
981 MH_SEND_CoopStats(ID);
983 if gGameSettings.GameMode = GM_CTF then
984 begin
985 if gFlags[FLAG_RED].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_RED].State, FLAG_RED, 0, True, ID);
986 if gFlags[FLAG_BLUE].State <> FLAG_STATE_CAPTURED then MH_SEND_FlagEvent(gFlags[FLAG_BLUE].State, FLAG_BLUE, 0, True, ID);
987 end;
989 if CreatePlayers and (ID >= 0) then NetClients[ID].State := NET_STATE_GAME;
991 g_Net_Flush();
992 end;
994 procedure MH_SEND_Info(ID: Byte);
995 begin
996 NetOut.Clear();
998 NetOut.Write(Byte(NET_MSG_INFO));
999 NetOut.Write(ID);
1000 NetOut.Write(NetClients[ID].Player);
1001 NetOut.Write(ExtractFileName(gGameSettings.WAD));
1002 NetOut.Write(g_ExtractFileName(gMapInfo.Map));
1003 NetOut.Write(gWADHash);
1004 NetOut.Write(gGameSettings.GameMode);
1005 NetOut.Write(gGameSettings.ScoreLimit);
1006 NetOut.Write(gGameSettings.TimeLimit);
1007 NetOut.Write(gGameSettings.MaxLives);
1008 NetOut.Write(gGameSettings.Options);
1009 NetOut.Write(gTime);
1011 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
1012 end;
1014 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
1015 var
1016 Name: string;
1017 i: Integer;
1018 Team: Byte;
1019 begin
1020 if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
1021 Mode := NET_CHAT_PLAYER;
1023 Team := 0;
1024 if (Mode = NET_CHAT_TEAM) then
1025 begin
1026 for i := Low(gPlayers) to High(gPlayers) do
1027 if (gPlayers[i] <> nil) and (gPlayers[i].FClientID >= 0) and
1028 (gPlayers[i].Team = ID) then
1029 begin
1030 NetOut.Write(Byte(NET_MSG_CHAT));
1031 NetOut.Write(Txt);
1032 NetOut.Write(Mode);
1033 g_Net_Host_Send(gPlayers[i].FClientID, True, NET_CHAN_CHAT);
1034 end;
1035 Team := ID;
1036 ID := NET_EVERYONE;
1037 end
1038 else
1039 begin
1040 NetOut.Write(Byte(NET_MSG_CHAT));
1041 NetOut.Write(Txt);
1042 NetOut.Write(Mode);
1043 g_Net_Host_Send(ID, True, NET_CHAN_CHAT);
1044 end;
1046 if Mode = NET_CHAT_SYSTEM then
1047 Exit;
1049 if ID = NET_EVERYONE then
1050 begin
1051 if Mode = NET_CHAT_PLAYER then
1052 begin
1053 g_Console_Add(Txt, True);
1054 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1055 g_Game_ChatSound(b_Text_Unformat(Txt));
1056 end
1057 else
1058 if Mode = NET_CHAT_TEAM then
1059 if gPlayer1 <> nil then
1060 begin
1061 if (gPlayer1.Team = TEAM_RED) and (Team = TEAM_RED) then
1062 begin
1063 g_Console_Add(#18'[Team] '#2 + Txt, True);
1064 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1065 g_Game_ChatSound(b_Text_Unformat(Txt));
1066 end
1067 else if (gPlayer1.Team = TEAM_BLUE) and (Team = TEAM_BLUE) then
1068 begin
1069 g_Console_Add(#20'[Team] '#2 + Txt, True);
1070 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1071 g_Game_ChatSound(b_Text_Unformat(Txt));
1072 end;
1073 end;
1074 end
1075 else
1076 begin
1077 Name := g_Net_ClientName_ByID(ID);
1078 g_Console_Add('-> ' + Name + ': ' + Txt, True);
1079 e_WriteLog('[Tell ' + Name + '] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1080 g_Game_ChatSound(b_Text_Unformat(Txt), False);
1081 end;
1082 end;
1084 procedure MH_SEND_Effect(X, Y: Integer; Ang: SmallInt; Kind: Byte; ID: Integer = NET_EVERYONE);
1085 begin
1086 NetOut.Write(Byte(NET_MSG_GFX));
1087 NetOut.Write(Kind);
1088 NetOut.Write(X);
1089 NetOut.Write(Y);
1090 NetOut.Write(Ang);
1092 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
1093 end;
1095 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
1096 begin
1097 NetOut.Write(Byte(NET_MSG_SND));
1098 NetOut.Write(Name);
1099 if Pos then
1100 begin
1101 NetOut.Write(Byte(1));
1102 NetOut.Write(X);
1103 NetOut.Write(Y);
1104 end
1105 else
1106 NetOut.Write(Byte(0));
1108 g_Net_Host_Send(ID, False, NET_CHAN_GAME);
1109 end;
1111 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
1112 begin
1113 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
1115 NetOut.Write(Byte(NET_MSG_SHADD));
1116 NetOut.Write(Proj);
1117 NetOut.Write(Shots[Proj].ShotType);
1118 NetOut.Write(Shots[Proj].Target);
1119 NetOut.Write(Shots[Proj].SpawnerUID);
1120 NetOut.Write(Shots[Proj].Timeout);
1121 NetOut.Write(Shots[Proj].Obj.X);
1122 NetOut.Write(Shots[Proj].Obj.Y);
1123 NetOut.Write(Shots[Proj].Obj.Vel.X);
1124 NetOut.Write(Shots[Proj].Obj.Vel.Y);
1126 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1127 end;
1129 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
1130 begin
1131 if (Shots = nil) or (Proj < 0) or (Proj > High(Shots)) then Exit;
1133 NetOut.Write(Byte(NET_MSG_SHPOS));
1134 NetOut.Write(Proj);
1135 NetOut.Write(Shots[Proj].Obj.X);
1136 NetOut.Write(Shots[Proj].Obj.Y);
1137 NetOut.Write(Shots[Proj].Obj.Vel.X);
1138 NetOut.Write(Shots[Proj].Obj.Vel.Y);
1140 g_Net_Host_Send(ID, False, NET_CHAN_SHOTS);
1141 end;
1143 procedure MH_Send_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
1144 begin
1145 NetOut.Write(Byte(NET_MSG_SHDEL));
1146 NetOut.Write(Proj);
1147 NetOut.Write(Byte(Loud));
1148 NetOut.Write(X);
1149 NetOut.Write(Y);
1151 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1152 end;
1154 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
1155 begin
1156 NetOut.Write(Byte(NET_MSG_SCORE));
1157 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1158 begin
1159 NetOut.Write(gTeamStat[TEAM_RED].Score);
1160 NetOut.Write(gTeamStat[TEAM_BLUE].Score);
1161 end
1162 else
1163 if gGameSettings.GameMode = GM_COOP then
1164 begin
1165 NetOut.Write(gCoopMonstersKilled);
1166 NetOut.Write(gCoopSecretsFound);
1167 end;
1169 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1170 end;
1172 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
1173 begin
1174 NetOut.Write(Byte(NET_MSG_COOP));
1175 NetOut.Write(gTotalMonsters);
1176 NetOut.Write(gSecretsCount);
1177 NetOut.Write(gCoopTotalMonstersKilled);
1178 NetOut.Write(gCoopTotalSecretsFound);
1179 NetOut.Write(gCoopTotalMonsters);
1180 NetOut.Write(gCoopTotalSecrets);
1181 end;
1183 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
1184 begin
1185 NetOut.Write(Byte(NET_MSG_GEVENT));
1186 NetOut.Write(EvType);
1187 NetOut.Write(EvNum);
1188 NetOut.Write(EvStr);
1189 NetOut.Write(Byte(gLastMap));
1190 NetOut.Write(gTime);
1191 if (EvType = NET_EV_MAPSTART) and isWadPath(EvStr) then
1192 begin
1193 NetOut.Write(Byte(1));
1194 NetOut.Write(gWADHash);
1195 end else
1196 NetOut.Write(Byte(0));
1198 g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
1199 end;
1201 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
1202 begin
1203 NetOut.Write(Byte(NET_MSG_FLAG));
1204 NetOut.Write(EvType);
1205 NetOut.Write(Flag);
1206 NetOut.Write(Byte(Quiet));
1207 NetOut.Write(PID);
1208 NetOut.Write(gFlags[Flag].State);
1209 NetOut.Write(gFlags[Flag].CaptureTime);
1210 NetOut.Write(gFlags[Flag].Obj.X);
1211 NetOut.Write(gFlags[Flag].Obj.Y);
1212 NetOut.Write(gFlags[Flag].Obj.Vel.X);
1213 NetOut.Write(gFlags[Flag].Obj.Vel.Y);
1214 NetOut.Write(Byte(gFlags[Flag].Direction));
1216 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1217 end;
1219 procedure MH_SEND_FlagPos(Flag: Byte; ID: Integer = NET_EVERYONE);
1220 begin
1221 NetOut.Write(Byte(NET_MSG_FLAGPOS));
1222 NetOut.Write(Flag);
1223 NetOut.Write(gFlags[Flag].Obj.X);
1224 NetOut.Write(gFlags[Flag].Obj.Y);
1225 NetOut.Write(gFlags[Flag].Obj.Vel.X);
1226 NetOut.Write(gFlags[Flag].Obj.Vel.Y);
1228 g_Net_Host_Send(ID, False, NET_CHAN_IMPORTANT);
1229 end;
1231 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
1232 begin
1233 NetOut.Write(Byte(NET_MSG_GSET));
1234 NetOut.Write(gGameSettings.GameMode);
1235 NetOut.Write(gGameSettings.ScoreLimit);
1236 NetOut.Write(gGameSettings.TimeLimit);
1237 NetOut.Write(gGameSettings.MaxLives);
1238 NetOut.Write(gGameSettings.Options);
1240 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1241 end;
1243 // PLAYER (SEND)
1245 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
1246 var
1247 P: TPlayer;
1248 begin
1249 P := g_Player_Get(PID);
1250 if P = nil then Exit;
1252 NetOut.Write(Byte(NET_MSG_PLR));
1253 NetOut.Write(PID);
1254 NetOut.Write(P.Name);
1256 NetOut.Write(P.FActualModelName);
1257 NetOut.Write(P.FColor.R);
1258 NetOut.Write(P.FColor.G);
1259 NetOut.Write(P.FColor.B);
1260 NetOut.Write(P.Team);
1262 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT)
1263 end;
1265 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
1266 var
1267 kByte: Word;
1268 Pl: TPlayer;
1269 begin
1270 Pl := g_Player_Get(PID);
1271 if Pl = nil then Exit;
1272 if Pl.FDummy then Exit;
1274 NetOut.Write(Byte(NET_MSG_PLRPOS));
1275 NetOut.Write(gTime);
1276 NetOut.Write(PID);
1278 kByte := 0;
1280 with Pl do
1281 begin
1282 NetOut.Write(FPing);
1283 NetOut.Write(FLoss);
1284 if IsKeyPressed(KEY_CHAT) then
1285 kByte := NET_KEY_CHAT
1286 else
1287 begin
1288 if IsKeyPressed(KEY_LEFT) then kByte := kByte or NET_KEY_LEFT;
1289 if IsKeyPressed(KEY_RIGHT) then kByte := kByte or NET_KEY_RIGHT;
1290 if IsKeyPressed(KEY_UP) then kByte := kByte or NET_KEY_UP;
1291 if IsKeyPressed(KEY_DOWN) then kByte := kByte or NET_KEY_DOWN;
1292 if IsKeyPressed(KEY_JUMP) then kByte := kByte or NET_KEY_JUMP;
1293 end;
1295 if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
1297 NetOut.Write(kByte);
1298 if Direction = TDirection.D_LEFT then NetOut.Write(Byte(0)) else NetOut.Write(Byte(1));
1299 NetOut.Write(GameX);
1300 NetOut.Write(GameY);
1301 NetOut.Write(GameVelX);
1302 NetOut.Write(GameVelY);
1303 NetOut.Write(GameAccelX);
1304 NetOut.Write(GameAccelY);
1305 end;
1307 g_Net_Host_Send(ID, Reliable, NET_CHAN_PLAYERPOS);
1308 end;
1310 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
1311 var
1312 P: TPlayer;
1313 I: Integer;
1314 begin
1315 P := g_Player_Get(PID);
1316 if P = nil then Exit;
1318 NetOut.Write(Byte(NET_MSG_PLRSTA));
1319 NetOut.Write(PID);
1321 with P do
1322 begin
1323 NetOut.Write(Byte(alive));
1324 NetOut.Write(Byte(GodMode));
1325 NetOut.Write(Health);
1326 NetOut.Write(Armor);
1327 NetOut.Write(Air);
1328 NetOut.Write(JetFuel);
1329 NetOut.Write(Lives);
1330 NetOut.Write(Team);
1332 for I := WP_FIRST to WP_LAST do
1333 NetOut.Write(Byte(FWeapon[I]));
1335 for I := A_BULLETS to A_HIGH do
1336 NetOut.Write(FAmmo[I]);
1338 for I := A_BULLETS to A_HIGH do
1339 NetOut.Write(FMaxAmmo[I]);
1341 for I := MR_SUIT to MR_MAX do
1342 NetOut.Write(LongWord(FMegaRulez[I]));
1344 NetOut.Write(Byte(R_ITEM_BACKPACK in FRulez));
1345 NetOut.Write(Byte(R_KEY_RED in FRulez));
1346 NetOut.Write(Byte(R_KEY_GREEN in FRulez));
1347 NetOut.Write(Byte(R_KEY_BLUE in FRulez));
1348 NetOut.Write(Byte(R_BERSERK in FRulez));
1350 NetOut.Write(Frags);
1351 NetOut.Write(Death);
1353 NetOut.Write(CurrWeap);
1355 NetOut.Write(Byte(FSpectator));
1356 NetOut.Write(Byte(FGhost));
1357 NetOut.Write(Byte(FPhysics));
1358 NetOut.Write(Byte(FNoRespawn));
1359 NetOut.Write(Byte(FJetpack));
1360 NetOut.Write(FFireTime);
1361 NetOut.Write(Byte(FFlaming));
1362 NetOut.Write(FSpawnInvul);
1363 end;
1365 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1366 end;
1368 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
1369 begin
1370 NetOut.Write(Byte(NET_MSG_PLRDMG));
1371 NetOut.Write(PID);
1372 NetOut.Write(Kind);
1373 NetOut.Write(Attacker);
1374 NetOut.Write(Value);
1375 NetOut.Write(VX);
1376 NetOut.Write(VY);
1378 g_Net_Host_Send(ID, False, NET_CHAN_PLAYER);
1379 end;
1381 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
1382 begin
1383 NetOut.Write(Byte(NET_MSG_PLRDIE));
1384 NetOut.Write(PID);
1385 NetOut.Write(KillType);
1386 NetOut.Write(DeathType);
1387 NetOut.Write(Attacker);
1389 g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
1390 end;
1392 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
1393 begin
1394 NetOut.Write(Byte(NET_MSG_PLRFIRE));
1395 NetOut.Write(PID);
1396 NetOut.Write(Weapon);
1397 NetOut.Write(X);
1398 NetOut.Write(Y);
1399 NetOut.Write(AX);
1400 NetOut.Write(AY);
1401 NetOut.Write(ShotID);
1403 g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
1404 end;
1406 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
1407 begin
1408 NetOut.Write(Byte(NET_MSG_PLRDEL));
1409 NetOut.Write(PID);
1411 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1412 end;
1414 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
1415 var
1416 Pl: TPlayer;
1417 begin
1418 Pl := g_Player_Get(PID);
1419 if Pl = nil then Exit;
1421 NetOut.Write(Byte(NET_MSG_PLRSET));
1422 NetOut.Write(PID);
1423 NetOut.Write(Pl.Name);
1424 if Mdl = '' then
1425 NetOut.Write(Pl.Model.GetName())
1426 else
1427 NetOut.Write(Mdl);
1428 NetOut.Write(Pl.FColor.R);
1429 NetOut.Write(Pl.FColor.G);
1430 NetOut.Write(Pl.FColor.B);
1431 NetOut.Write(Pl.Team);
1433 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1434 end;
1436 // ITEM (SEND)
1438 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1439 var
1440 it: PItem;
1441 tt: Byte;
1442 begin
1443 it := g_Items_ByIdx(IID);
1445 NetOut.Write(Byte(NET_MSG_ISPAWN));
1446 NetOut.Write(IID);
1447 NetOut.Write(Byte(Quiet));
1448 tt := it.ItemType;
1449 if it.dropped then tt := tt or $80;
1450 NetOut.Write(tt);
1451 NetOut.Write(Byte(it.Fall));
1452 NetOut.Write(Byte(it.Respawnable));
1453 NetOut.Write(it.Obj.X);
1454 NetOut.Write(it.Obj.Y);
1455 NetOut.Write(it.Obj.Vel.X);
1456 NetOut.Write(it.Obj.Vel.Y);
1458 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1459 end;
1461 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
1462 begin
1463 NetOut.Write(Byte(NET_MSG_IDEL));
1464 NetOut.Write(IID);
1465 NetOut.Write(Byte(Quiet));
1467 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1468 end;
1470 procedure MH_SEND_ItemPos(IID: Word; ID: Integer = NET_EVERYONE);
1471 var
1472 it: PItem;
1473 begin
1474 it := g_Items_ByIdx(IID);
1476 NetOut.Write(Byte(NET_MSG_IPOS));
1477 NetOut.Write(IID);
1478 NetOut.Write(it.Obj.X);
1479 NetOut.Write(it.Obj.Y);
1480 NetOut.Write(it.Obj.Vel.X);
1481 NetOut.Write(it.Obj.Vel.Y);
1483 g_Net_Host_Send(ID, False, NET_CHAN_LARGEDATA);
1484 end;
1486 // PANEL
1488 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
1489 var
1490 TP: TPanel;
1491 begin
1492 TP := g_Map_PanelByGUID(PGUID);
1493 if (TP = nil) then exit;
1495 with TP do
1496 begin
1497 NetOut.Write(Byte(NET_MSG_PTEX));
1498 NetOut.Write(LongWord(PGUID));
1499 NetOut.Write(FCurTexture);
1500 NetOut.Write(FCurFrame);
1501 NetOut.Write(FCurFrameCount);
1502 NetOut.Write(AnimLoop);
1503 end;
1505 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1506 end;
1508 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
1509 var
1510 TP: TPanel;
1511 mpflags: Byte = 0;
1512 begin
1513 TP := g_Map_PanelByGUID(PGUID);
1514 if (TP = nil) then exit;
1516 NetOut.Write(Byte(NET_MSG_PSTATE));
1517 NetOut.Write(LongWord(PGUID));
1518 NetOut.Write(Byte(TP.Enabled));
1519 NetOut.Write(TP.LiftType);
1520 NetOut.Write(TP.X);
1521 NetOut.Write(TP.Y);
1522 NetOut.Write(Word(TP.Width));
1523 NetOut.Write(Word(TP.Height));
1524 // mplats
1525 NetOut.Write(LongInt(TP.movingSpeedX));
1526 NetOut.Write(LongInt(TP.movingSpeedY));
1527 NetOut.Write(LongInt(TP.movingStartX));
1528 NetOut.Write(LongInt(TP.movingStartY));
1529 NetOut.Write(LongInt(TP.movingEndX));
1530 NetOut.Write(LongInt(TP.movingEndY));
1531 NetOut.Write(LongInt(TP.sizeSpeedX));
1532 NetOut.Write(LongInt(TP.sizeSpeedY));
1533 NetOut.Write(LongInt(TP.sizeEndX));
1534 NetOut.Write(LongInt(TP.sizeEndY));
1535 if TP.movingActive then mpflags := mpflags or 1;
1536 if TP.moveOnce then mpflags := mpflags or 2;
1537 NetOut.Write(Byte(mpflags));
1539 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1540 end;
1542 // TRIGGER
1544 procedure MH_SEND_TriggerSound(var T: TTrigger; ID: Integer = NET_EVERYONE);
1545 begin
1546 if gTriggers = nil then Exit;
1547 if T.Sound = nil then Exit;
1549 NetOut.Write(Byte(NET_MSG_TSOUND));
1550 NetOut.Write(T.ClientID);
1551 NetOut.Write(Byte(T.Sound.IsPlaying));
1552 NetOut.Write(LongWord(T.Sound.GetPosition));
1553 NetOut.Write(T.SoundPlayCount);
1555 g_Net_Host_Send(ID, True);
1556 end;
1558 procedure MH_SEND_TriggerMusic(ID: Integer = NET_EVERYONE);
1559 begin
1560 NetOut.Write(Byte(NET_MSG_TMUSIC));
1561 NetOut.Write(gMusic.Name);
1562 NetOut.Write(Byte(gMusic.IsPlaying));
1563 NetOut.Write(LongWord(gMusic.GetPosition));
1564 NetOut.Write(Byte(gMusic.SpecPause or gMusic.IsPaused));
1566 g_Net_Host_Send(ID, True);
1567 end;
1569 // MONSTER
1571 procedure MH_SEND_MonsterSpawn(UID: Word; ID: Integer = NET_EVERYONE);
1572 var
1573 M: TMonster;
1574 begin
1575 M := g_Monsters_ByUID(UID);
1576 if M = nil then
1577 Exit;
1579 with M do
1580 begin
1581 NetOut.Write(Byte(NET_MSG_MSPAWN));
1582 NetOut.Write(UID);
1583 NetOut.Write(MonsterType);
1584 NetOut.Write(MonsterState);
1585 NetOut.Write(MonsterAnim);
1586 NetOut.Write(MonsterTargetUID);
1587 NetOut.Write(MonsterTargetTime);
1588 NetOut.Write(MonsterBehaviour);
1589 NetOut.Write(MonsterSleep);
1590 NetOut.Write(MonsterHealth);
1591 NetOut.Write(MonsterAmmo);
1592 NetOut.Write(GameX);
1593 NetOut.Write(GameY);
1594 NetOut.Write(GameVelX);
1595 NetOut.Write(GameVelY);
1596 NetOut.Write(Byte(GameDirection));
1597 end;
1599 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1600 end;
1602 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
1603 var
1604 M: TMonster;
1605 begin
1606 M := g_Monsters_ByUID(UID);
1607 if M = nil then Exit;
1609 NetOut.Write(Byte(NET_MSG_MPOS));
1610 NetOut.Write(UID);
1612 with M do
1613 begin
1614 NetOut.Write(GameX);
1615 NetOut.Write(GameY);
1616 NetOut.Write(GameVelX);
1617 NetOut.Write(GameVelY);
1618 NetOut.Write(Byte(GameDirection));
1619 end;
1621 g_Net_Host_Send(ID, False, NET_CHAN_MONSTERPOS);
1622 end;
1624 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
1625 var
1626 M: TMonster;
1627 begin
1628 M := g_Monsters_ByUID(UID);
1629 if M = nil then Exit;
1631 NetOut.Write(Byte(NET_MSG_MSTATE));
1632 NetOut.Write(UID);
1634 with M do
1635 begin
1636 NetOut.Write(MonsterState);
1637 NetOut.Write(ForcedAnim);
1638 NetOut.Write(MonsterTargetUID);
1639 NetOut.Write(MonsterTargetTime);
1640 NetOut.Write(MonsterSleep);
1641 NetOut.Write(MonsterHealth);
1642 NetOut.Write(MonsterAmmo);
1643 NetOut.Write(MonsterPain);
1644 NetOut.Write(Byte(AnimIsReverse));
1645 NetOut.Write(FFireTime);
1646 end;
1648 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1649 end;
1651 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
1652 begin
1653 NetOut.Write(Byte(NET_MSG_MSHOT));
1654 NetOut.Write(UID);
1655 NetOut.Write(X);
1656 NetOut.Write(Y);
1657 NetOut.Write(VX);
1658 NetOut.Write(VY);
1660 g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
1661 end;
1663 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
1664 var
1665 M: TMonster;
1666 begin
1667 M := g_Monsters_ByUID(UID);
1668 if M = nil then Exit;
1670 NetOut.Write(Byte(NET_MSG_MDEL));
1671 NetOut.Write(UID);
1673 g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
1674 end;
1676 // MISC
1678 procedure MH_SEND_TimeSync(Time: LongWord; ID: Integer = NET_EVERYONE);
1679 begin
1680 NetOut.Write(Byte(NET_MSG_TIME_SYNC));
1681 NetOut.Write(Time);
1683 g_Net_Host_Send(ID, False, NET_CHAN_SERVICE);
1684 end;
1686 procedure MH_SEND_VoteEvent(EvType: Byte;
1687 StrArg1: string = 'a'; StrArg2: string = 'b';
1688 IntArg1: SmallInt = 0; IntArg2: SmallInt = 0;
1689 ID: Integer = NET_EVERYONE);
1690 begin
1691 NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
1692 NetOut.Write(EvType);
1693 NetOut.Write(IntArg1);
1694 NetOut.Write(IntArg2);
1695 NetOut.Write(StrArg1);
1696 NetOut.Write(StrArg2);
1698 g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
1699 end;
1701 // CLIENT MESSAGES //
1703 // GAME
1705 procedure MC_RECV_Chat(var M: TMsg);
1706 var
1707 Txt: string;
1708 Mode: Byte;
1709 begin
1710 Txt := M.ReadString();
1711 Mode := M.ReadByte();
1713 if Mode <> NET_CHAT_SYSTEM then
1714 begin
1715 if NetDeafLevel = 0 then
1716 begin
1717 if Mode = NET_CHAT_PLAYER then
1718 begin
1719 g_Console_Add(Txt, True);
1720 e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1721 g_Game_ChatSound(b_Text_Unformat(Txt));
1722 end else
1723 if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then
1724 begin
1725 if gPlayer1.Team = TEAM_RED then
1726 g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True);
1727 if gPlayer1.Team = TEAM_BLUE then
1728 g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True);
1729 e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
1730 g_Game_ChatSound(b_Text_Unformat(Txt));
1731 end;
1732 end;
1733 end else if (NetDeafLevel < 2) then
1734 g_Console_Add(Txt, True);
1735 end;
1737 procedure MC_RECV_Effect(var M: TMsg);
1738 var
1739 Kind: Byte;
1740 X, Y: Integer;
1741 Ang: SmallInt;
1742 begin
1743 if not gGameOn then Exit;
1744 Kind := M.ReadByte();
1745 X := M.ReadLongInt();
1746 Y := M.ReadLongInt();
1747 Ang := M.ReadSmallInt();
1749 case Kind of
1750 NET_GFX_SPARK:
1751 begin
1752 {$IFDEF ENABLE_GFX}
1753 g_GFX_Spark(X, Y, 2 + Random(2), Ang, 0, 0);
1754 {$ENDIF}
1755 end;
1756 NET_GFX_TELE:
1757 begin
1758 {$IFDEF ENABLE_GFX}
1759 g_GFX_QueueEffect(R_GFX_TELEPORT_FAST, X, Y);
1760 {$ENDIF}
1761 if Ang = 1 then
1762 g_Sound_PlayExAt('SOUND_GAME_TELEPORT', X, Y);
1763 end;
1764 NET_GFX_EXPLODE:
1765 begin
1766 {$IFDEF ENABLE_GFX}
1767 g_GFX_QueueEffect(R_GFX_EXPLODE_ROCKET, X - 64, Y - 64);
1768 {$ENDIF}
1769 if Ang = 1 then
1770 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEROCKET', X, Y);
1771 end;
1772 NET_GFX_BFGEXPL:
1773 begin
1774 {$IFDEF ENABLE_GFX}
1775 g_GFX_QueueEffect(R_GFX_EXPLODE_BFG, X - 64, Y - 64);
1776 {$ENDIF}
1777 if Ang = 1 then
1778 g_Sound_PlayExAt('SOUND_WEAPON_EXPLODEBFG', X, Y);
1779 end;
1780 NET_GFX_BFGHIT:
1781 begin
1782 {$IFDEF ENABLE_GFX}
1783 g_GFX_QueueEffect(R_GFX_BFG_HIT, X - 32, Y - 32);
1784 {$ENDIF}
1785 end;
1786 NET_GFX_FIRE:
1787 begin
1788 {$IFDEF ENABLE_GFX}
1789 g_GFX_QueueEffect(R_GFX_FIRE, X, Y);
1790 {$ENDIF}
1791 if Ang = 1 then
1792 g_Sound_PlayExAt('SOUND_FIRE', X, Y);
1793 end;
1794 NET_GFX_RESPAWN:
1795 begin
1796 {$IFDEF ENABLE_GFX}
1797 g_GFX_QueueEffect(R_GFX_ITEM_RESPAWN, X, Y);
1798 {$ENDIF}
1799 if Ang = 1 then
1800 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
1801 end;
1802 NET_GFX_SHELL1:
1803 begin
1804 {$IFDEF ENABLE_SHELLS}
1805 g_Shells_Create(X, Y, 0, -2, SHELL_BULLET);
1806 {$ENDIF}
1807 end;
1808 NET_GFX_SHELL2:
1809 begin
1810 {$IFDEF ENABLE_SHELLS}
1811 g_Shells_Create(X, Y, 0, -2, SHELL_SHELL);
1812 {$ENDIF}
1813 end;
1814 NET_GFX_SHELL3:
1815 begin
1816 {$IFDEF ENABLE_SHELLS}
1817 g_Shells_Create(X, Y, 0, -2, SHELL_SHELL);
1818 g_Shells_Create(X, Y, 0, -2, SHELL_SHELL);
1819 {$ENDIF}
1820 end;
1821 end;
1822 end;
1824 procedure MC_RECV_Sound(var M: TMsg);
1825 var
1826 Name: string;
1827 X, Y: Integer;
1828 Pos: Boolean;
1829 begin
1830 Name := M.ReadString();
1831 Pos := M.ReadByte() <> 0;
1832 if Pos then
1833 begin
1834 X := M.ReadLongInt();
1835 Y := M.ReadLongInt();
1836 g_Sound_PlayExAt(Name, X, Y);
1837 end
1838 else
1839 g_Sound_PlayEx(Name);
1840 end;
1842 procedure MC_RECV_CreateShot(var M: TMsg);
1843 var
1844 I, X, Y, XV, YV: Integer;
1845 Timeout: LongWord;
1846 Target, Spawner: Word;
1847 ShType: Byte;
1848 begin
1849 I := M.ReadLongInt();
1850 ShType := M.ReadByte();
1851 Target := M.ReadWord();
1852 Spawner := M.ReadWord();
1853 Timeout := M.ReadLongWord();
1854 X := M.ReadLongInt();
1855 Y := M.ReadLongInt();
1856 XV := M.ReadLongInt();
1857 YV := M.ReadLongInt();
1859 I := g_Weapon_CreateShot(I, ShType, Spawner, Target, X, Y, XV, YV);
1860 if (Shots <> nil) and (I <= High(Shots)) then
1861 begin
1862 Shots[I].Timeout := Timeout;
1863 //Shots[I].Target := Target; // TODO: find a use for Target later
1864 end;
1865 end;
1867 procedure MC_RECV_UpdateShot(var M: TMsg);
1868 var
1869 I, TX, TY, TXV, TYV: Integer;
1870 begin
1871 I := M.ReadLongInt();
1872 TX := M.ReadLongInt();
1873 TY := M.ReadLongInt();
1874 TXV := M.ReadLongInt();
1875 TYV := M.ReadLongInt();
1877 if (Shots <> nil) and (I <= High(Shots)) then
1878 with (Shots[i]) do
1879 begin
1880 Obj.X := TX;
1881 Obj.Y := TY;
1882 Obj.Vel.X := TXV;
1883 Obj.Vel.Y := TYV;
1884 end;
1885 end;
1887 procedure MC_RECV_DeleteShot(var M: TMsg);
1888 var
1889 I, X, Y: Integer;
1890 L: Boolean;
1891 begin
1892 if not gGameOn then Exit;
1893 I := M.ReadLongInt();
1894 L := (M.ReadByte() <> 0);
1895 X := M.ReadLongInt();
1896 Y := M.ReadLongInt();
1898 g_Weapon_DestroyShot(I, X, Y, L);
1899 end;
1901 procedure MC_RECV_GameStats(var M: TMsg);
1902 begin
1903 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
1904 begin
1905 gTeamStat[TEAM_RED].Score := M.ReadSmallInt();
1906 gTeamStat[TEAM_BLUE].Score := M.ReadSmallInt();
1907 end
1908 else
1909 if gGameSettings.GameMode = GM_COOP then
1910 begin
1911 gCoopMonstersKilled := M.ReadWord();
1912 gCoopSecretsFound := M.ReadWord();
1913 end;
1914 end;
1916 procedure MC_RECV_CoopStats(var M: TMsg);
1917 begin
1918 gTotalMonsters := M.ReadLongInt();
1919 gSecretsCount := M.ReadLongInt();
1920 gCoopTotalMonstersKilled := M.ReadWord();
1921 gCoopTotalSecretsFound := M.ReadWord();
1922 gCoopTotalMonsters := M.ReadWord();
1923 gCoopTotalSecrets := M.ReadWord();
1924 end;
1926 procedure MC_RECV_GameEvent(var M: TMsg);
1927 var
1928 EvType: Byte;
1929 EvNum: Integer;
1930 EvStr: string;
1931 EvTime: LongWord;
1932 BHash: Boolean;
1933 EvHash: TMD5Digest;
1934 pl: TPlayer;
1935 i1, i2: TStrings_Locale;
1936 pln: String;
1937 cnt: Byte;
1938 goodCmd: Boolean = true;
1939 begin
1940 FillChar(EvHash, Sizeof(EvHash), 0);
1941 EvType := M.ReadByte();
1942 EvNum := M.ReadLongInt();
1943 EvStr := M.ReadString();
1944 gLastMap := M.ReadByte() <> 0;
1945 if gLastMap and (gGameSettings.GameMode = GM_COOP) then gStatsOff := True;
1946 gStatsPressed := True;
1947 EvTime := M.ReadLongWord();
1948 BHash := M.ReadByte() <> 0;
1949 if BHash then
1950 EvHash := M.ReadMD5();
1952 gTime := EvTime;
1954 if (g_Res_received_map_start <> 0) then
1955 begin
1956 if (g_Res_received_map_start < 0) then exit;
1957 goodCmd := false;
1958 case EvType of
1959 NET_EV_MAPSTART: goodCmd := true;
1960 NET_EV_MAPEND: goodCmd := true;
1961 NET_EV_PLAYER_KICK: goodCmd := true;
1962 NET_EV_PLAYER_BAN: goodCmd := true;
1963 NET_EV_LMS_WARMUP: goodCmd := true;
1964 end;
1965 if not goodCmd then exit;
1966 end;
1968 case EvType of
1969 NET_EV_MAPSTART:
1970 begin
1971 if (g_Res_received_map_start <> 0) then
1972 begin
1973 g_Res_received_map_start := -1;
1974 end
1975 else
1976 begin
1977 gGameOn := False;
1978 g_Game_ClearLoading();
1979 g_Game_StopAllSounds(True);
1981 gSwitchGameMode := Byte(EvNum);
1982 gGameSettings.GameMode := gSwitchGameMode;
1984 gWADHash := EvHash;
1985 if not g_Game_StartMap(false{asMegawad}, EvStr, True) then
1986 begin
1987 if not isWadPath(EvStr) then
1988 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [gGameSettings.WAD + ':\' + EvStr]))
1989 else
1990 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [EvStr]));
1991 Exit;
1992 end;
1994 MC_SEND_FullStateRequest;
1995 end;
1996 end;
1998 NET_EV_MAPEND:
1999 begin
2000 gLMSRespawn := LMS_RESPAWN_NONE;
2001 gLMSRespawnTime := 0;
2002 if (g_Res_received_map_start <> 0) then
2003 begin
2004 g_Res_received_map_start := -1;
2005 end
2006 else
2007 begin
2008 gMissionFailed := EvNum <> 0;
2009 gExit := EXIT_ENDLEVELCUSTOM;
2010 end;
2011 end;
2013 NET_EV_RCON:
2014 begin
2015 case EvNum of
2016 NET_RCON_NOAUTH:
2017 g_Console_Add(_lc[I_NET_RCON_NOAUTH], True);
2018 NET_RCON_PWGOOD:
2019 g_Console_Add(_lc[I_NET_RCON_PWD_VALID], True);
2020 NET_RCON_PWBAD:
2021 g_Console_Add(_lc[I_NET_RCON_PWD_INVALID], True);
2022 end;
2023 end;
2025 NET_EV_CHANGE_TEAM:
2026 begin
2027 if NetDeafLevel < 2 then
2028 begin
2029 if EvNum = TEAM_RED then
2030 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True);
2031 if EvNum = TEAM_BLUE then
2032 g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True);
2033 end;
2034 end;
2036 NET_EV_PLAYER_KICK:
2037 begin
2038 g_Console_Add(Format(_lc[I_PLAYER_KICK], [EvStr]), True);
2039 if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1;
2040 end;
2042 NET_EV_PLAYER_BAN:
2043 begin
2044 g_Console_Add(Format(_lc[I_PLAYER_BAN], [EvStr]), True);
2045 if (g_Res_received_map_start <> 0) then g_Res_received_map_start := -1;
2046 end;
2048 NET_EV_LMS_WARMUP:
2049 begin
2050 if EvNum > 0 then
2051 begin
2052 gLMSRespawn := LMS_RESPAWN_WARMUP;
2053 gLMSRespawnTime := gTime + EvNum;
2054 g_Console_Add(Format(_lc[I_MSG_WARMUP_START], [EvNum div 1000]), True);
2055 end
2056 else if gPlayer1 = nil then
2057 begin
2058 g_Console_Add(_lc[I_PLAYER_SPECT4], True);
2059 end;
2060 end;
2062 NET_EV_LMS_SURVIVOR:
2063 g_Console_Add('*** ' + _lc[I_MESSAGE_LMS_SURVIVOR] + ' ***', True);
2065 NET_EV_BIGTEXT:
2066 if NetDeafLevel < 2 then g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum));
2068 NET_EV_SCORE:
2069 begin
2070 pl := g_Player_Get(EvNum and $FFFF);
2071 if pl = nil then
2072 pln := '?'
2073 else
2074 pln := pl.Name;
2075 cnt := (EvNum shr 16) and $FF;
2076 if Pos('w', EvStr) = 0 then
2077 begin
2078 // Default score
2079 if Pos('t', EvStr) = 0 then
2080 begin
2081 // Player +/- score
2082 if Pos('-', EvStr) = 0 then
2083 begin
2084 if Pos('e', EvStr) = 0 then
2085 i1 := I_PLAYER_SCORE_ADD_OWN
2086 else
2087 i1 := I_PLAYER_SCORE_ADD_ENEMY;
2088 end else
2089 begin
2090 if Pos('e', EvStr) = 0 then
2091 i1 := I_PLAYER_SCORE_SUB_OWN
2092 else
2093 i1 := I_PLAYER_SCORE_SUB_ENEMY;
2094 end;
2095 // Which team
2096 if Pos('r', EvStr) > 0 then
2097 i2 := I_PLAYER_SCORE_TO_RED
2098 else
2099 i2 := I_PLAYER_SCORE_TO_BLUE;
2100 g_Console_Add(Format(_lc[i1], [pln, cnt, _lc[i2]]), True);
2101 end else
2102 begin
2103 // Team +/- score
2104 if Pos('-', EvStr) = 0 then
2105 i1 := I_PLAYER_SCORE_ADD_TEAM
2106 else
2107 i1 := I_PLAYER_SCORE_SUB_TEAM;
2108 // Which team
2109 if Pos('r', EvStr) > 0 then
2110 i2 := I_PLAYER_SCORE_RED
2111 else
2112 i2 := I_PLAYER_SCORE_BLUE;
2113 g_Console_Add(Format(_lc[i1], [_lc[i2], cnt]), True);
2114 end;
2115 end else
2116 begin
2117 // Game Win
2118 if Pos('e', EvStr) = 0 then
2119 i1 := I_PLAYER_SCORE_WIN_OWN
2120 else
2121 i1 := I_PLAYER_SCORE_WIN_ENEMY;
2122 // Which team
2123 if Pos('r', EvStr) > 0 then
2124 i2 := I_PLAYER_SCORE_TO_RED
2125 else
2126 i2 := I_PLAYER_SCORE_TO_BLUE;
2127 g_Console_Add(Format(_lc[i1], [pln, _lc[i2]]), True);
2128 end;
2129 end;
2131 NET_EV_SCORE_MSG:
2132 begin
2133 if EvNum = TEAM_RED then
2134 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
2135 if EvNum = TEAM_BLUE then
2136 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_ADD], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
2137 if EvNum = -TEAM_RED then
2138 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 108);
2139 if EvNum = -TEAM_BLUE then
2140 g_Game_Message(Format(_lc[I_MESSAGE_SCORE_SUB], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 108);
2141 end;
2143 NET_EV_LMS_START:
2144 begin
2145 {$IFDEF ENABLE_GIBS}
2146 g_Gibs_RemoveAll;
2147 {$ENDIF}
2148 {$IFDEF ENALBE_SHELLS}
2149 g_Shells_RemoveAll;
2150 {$ENDIF}
2151 {$IFDEF ENABLE_CORPSES}
2152 g_Corpses_RemoveAll;
2153 {$ENDIF}
2154 gLMSRespawn := LMS_RESPAWN_NONE;
2155 g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
2156 end;
2158 NET_EV_LMS_WIN:
2159 g_Game_Message(Format(_lc[I_MESSAGE_LMS_WIN], [AnsiUpperCase(EvStr)]), 144);
2161 NET_EV_TLMS_WIN:
2162 begin
2163 if EvNum = TEAM_RED then
2164 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_RED])]), 144);
2165 if EvNum = TEAM_BLUE then
2166 g_Game_Message(Format(_lc[I_MESSAGE_TLMS_WIN], [AnsiUpperCase(_lc[I_GAME_TEAM_BLUE])]), 144);
2167 end;
2169 NET_EV_LMS_LOSE:
2170 g_Game_Message(_lc[I_MESSAGE_LMS_LOSE], 144);
2172 NET_EV_LMS_DRAW:
2173 g_Game_Message(_lc[I_GAME_WIN_DRAW], 144);
2175 NET_EV_LMS_NOSPAWN:
2176 g_Console_Add(_lc[I_PLAYER_SPECT4], True);
2178 NET_EV_KILLCOMBO:
2179 g_Game_Announce_KillCombo(EvNum);
2181 NET_EV_PLAYER_TOUCH:
2182 begin
2183 pl := g_Player_Get(EvNum);
2184 if pl <> nil then
2185 pl.Touch();
2186 end;
2188 NET_EV_SECRET:
2189 begin
2190 pl := g_Player_Get(EvNum);
2191 if pl <> nil then
2192 begin
2193 g_Console_Add(Format(_lc[I_PLAYER_SECRET], [pl.Name]), True);
2194 g_Sound_PlayEx('SOUND_GAME_SECRET');
2195 end;
2196 end;
2198 NET_EV_INTER_READY:
2199 begin
2200 pl := g_Player_Get(EvNum);
2201 if pl <> nil then pl.FReady := (EvStr = 'Y');
2202 end;
2203 end;
2204 end;
2206 procedure MC_RECV_FlagPos(var M: TMsg);
2207 var
2208 Fl: Byte;
2209 begin
2210 Fl := M.ReadByte();
2211 if Fl = FLAG_NONE then Exit;
2212 gFlags[Fl].Obj.X := M.ReadLongInt();
2213 gFlags[Fl].Obj.Y := M.ReadLongInt();
2214 gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
2215 gFlags[Fl].Obj.Vel.Y := M.ReadLongInt();
2216 end;
2218 procedure MC_RECV_FlagEvent(var M: TMsg);
2219 var
2220 PID: Word;
2221 Pl: TPlayer;
2222 EvType: Byte;
2223 Fl, a: Byte;
2224 Quiet: Boolean;
2225 s, ts: string;
2226 begin
2227 EvType := M.ReadByte();
2228 Fl := M.ReadByte();
2230 if Fl = FLAG_NONE then Exit;
2232 Quiet := (M.ReadByte() <> 0);
2233 PID := M.ReadWord();
2235 gFlags[Fl].State := M.ReadByte();
2236 gFlags[Fl].CaptureTime := M.ReadLongWord();
2237 gFlags[Fl].Obj.X := M.ReadLongInt();
2238 gFlags[Fl].Obj.Y := M.ReadLongInt();
2239 gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
2240 gFlags[Fl].Obj.Vel.Y := M.ReadLongInt();
2241 gFlags[Fl].Direction := TDirection(M.ReadByte());
2243 Pl := g_Player_Get(PID);
2244 if (Pl = nil) and
2245 (EvType <> FLAG_STATE_NORMAL) and
2246 (EvType <> FLAG_STATE_DROPPED) and
2247 (EvType <> FLAG_STATE_RETURNED) then
2248 Exit;
2250 case EvType of
2251 FLAG_STATE_NORMAL:
2252 begin
2253 if Quiet or (Pl = nil) then Exit;
2255 if Fl = FLAG_RED then
2256 s := _lc[I_PLAYER_FLAG_RED]
2257 else
2258 s := _lc[I_PLAYER_FLAG_BLUE];
2260 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2262 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2263 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2264 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2265 a := 0
2266 else
2267 a := 1;
2269 if not sound_ret_flag[a].IsPlaying() then
2270 sound_ret_flag[a].Play();
2271 end;
2273 FLAG_STATE_CAPTURED:
2274 begin
2275 if (Pl <> nil) then Pl.SetFlag(Fl);
2277 if Quiet then Exit;
2279 if Fl = FLAG_RED then
2280 s := _lc[I_PLAYER_FLAG_RED]
2281 else
2282 s := _lc[I_PLAYER_FLAG_BLUE];
2284 g_Console_Add(Format(_lc[I_PLAYER_FLAG_GET], [Pl.Name, s]), True);
2285 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_GET], [AnsiUpperCase(s)]), 144);
2287 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2288 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2289 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2290 a := 0
2291 else
2292 a := 1;
2294 if not sound_get_flag[a].IsPlaying() then
2295 sound_get_flag[a].Play();
2296 end;
2298 FLAG_STATE_DROPPED:
2299 begin
2300 if (Pl <> nil) then Pl.SetFlag(FLAG_NONE);
2302 if Quiet or (Pl = nil) then Exit;
2304 if Fl = FLAG_RED then
2305 s := _lc[I_PLAYER_FLAG_RED]
2306 else
2307 s := _lc[I_PLAYER_FLAG_BLUE];
2309 g_Console_Add(Format(_lc[I_PLAYER_FLAG_DROP], [Pl.Name, s]), True);
2310 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_DROP], [AnsiUpperCase(s)]), 144);
2312 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2313 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2314 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2315 a := 0
2316 else
2317 a := 1;
2319 if not sound_lost_flag[a].IsPlaying() then
2320 sound_lost_flag[a].Play();
2321 end;
2323 FLAG_STATE_SCORED:
2324 begin
2325 g_Map_ResetFlag(FLAG_RED);
2326 g_Map_ResetFlag(FLAG_BLUE);
2327 if Quiet or (Pl = nil) then Exit;
2328 Pl.SetFlag(FLAG_NONE);
2330 if Fl = FLAG_RED then
2331 s := _lc[I_PLAYER_FLAG_RED]
2332 else
2333 s := _lc[I_PLAYER_FLAG_BLUE];
2335 ts := Format('%.4d', [gFlags[Fl].CaptureTime]);
2336 Insert('.', ts, Length(ts) + 1 - 3);
2337 g_Console_Add(Format(_lc[I_PLAYER_FLAG_CAPTURE], [Pl.Name, s, ts]), True);
2338 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_CAPTURE], [AnsiUpperCase(s)]), 144);
2340 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2341 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2342 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2343 a := 0
2344 else
2345 a := 1;
2347 if not sound_cap_flag[a].IsPlaying() then
2348 sound_cap_flag[a].Play();
2349 end;
2351 FLAG_STATE_RETURNED:
2352 begin
2353 g_Map_ResetFlag(Fl);
2354 if Quiet then Exit;
2356 if Fl = FLAG_RED then
2357 s := _lc[I_PLAYER_FLAG_RED]
2358 else
2359 s := _lc[I_PLAYER_FLAG_BLUE];
2361 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2363 if ((Pl = gPlayer1) or (Pl = gPlayer2)
2364 or ((gPlayer1 <> nil) and (gPlayer1.Team = Pl.Team))
2365 or ((gPlayer2 <> nil) and (gPlayer2.Team = Pl.Team))) then
2366 a := 0
2367 else
2368 a := 1;
2370 if not sound_ret_flag[a].IsPlaying() then
2371 sound_ret_flag[a].Play();
2372 end;
2373 end;
2374 end;
2376 procedure MC_RECV_GameSettings(var M: TMsg);
2377 begin
2378 gGameSettings.GameMode := M.ReadByte();
2379 gGameSettings.ScoreLimit := M.ReadWord();
2380 gGameSettings.TimeLimit := M.ReadWord();
2381 gGameSettings.MaxLives := M.ReadByte();
2382 gGameSettings.Options := M.ReadLongWord();
2383 end;
2385 // PLAYER
2387 function MC_RECV_PlayerCreate(var M: TMsg): Word;
2388 var
2389 PID, DID: Word;
2390 PName, Model: string;
2391 Color: TRGB;
2392 T: Byte;
2393 Pl: TPlayer;
2394 begin
2395 PID := M.ReadWord();
2396 Pl := g_Player_Get(PID);
2398 PName := M.ReadString();
2399 Model := M.ReadString();
2400 Color.R := M.ReadByte();
2401 Color.G := M.ReadByte();
2402 Color.B := M.ReadByte();
2403 T := M.ReadByte();
2405 Result := 0;
2406 if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
2407 begin
2408 if (Pl <> nil) then Exit;
2409 if (g_Force_Model_Get() <> 0) then
2410 Model := g_Forced_Model_GetName();
2411 DID := g_Player_Create(Model, Color, T, False);
2412 with g_Player_Get(DID) do
2413 begin
2414 UID := PID;
2415 Name := PName;
2416 Reset(True);
2417 end;
2418 end
2419 else
2420 begin
2421 if (PID = NetPlrUID1) and (gPlayer1 <> nil) then begin
2422 gPlayer1.UID := PID;
2423 gPlayer1.Model.SetColor(Color.R, Color.G, Color.B);
2424 gPlayer1.ChangeTeam(T);
2425 end;
2426 if (PID = NetPlrUID2) and (gPlayer2 <> nil) then begin
2427 gPlayer2.UID := PID;
2428 gPlayer2.Model.SetColor(Color.R, Color.G, Color.B);
2429 gPlayer2.ChangeTeam(T);
2430 end;
2431 end;
2433 if NetDeafLevel < 3 then
2434 g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
2435 e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', TMsgType.Notify);
2436 Result := PID;
2437 end;
2439 function MC_RECV_PlayerPos(var M: TMsg): Word;
2440 var
2441 GT: LongWord;
2442 PID: Word;
2443 kByte: Word;
2444 Pl: TPlayer;
2445 Dir: Byte;
2446 TmpX, TmpY: Integer;
2447 begin
2448 Result := 0;
2450 GT := M.ReadLongWord();
2451 if GT < gTime - NET_MAX_DIFFTIME then
2452 begin
2453 gTime := GT;
2454 Exit;
2455 end;
2456 gTime := GT;
2458 PID := M.ReadWord();
2459 Pl := g_Player_Get(PID);
2461 if Pl = nil then Exit;
2463 Result := PID;
2465 with Pl do
2466 begin
2467 FPing := M.ReadWord();
2468 FLoss := M.ReadByte();
2469 kByte := M.ReadWord();
2470 Dir := M.ReadByte();
2472 TmpX := M.ReadLongInt();
2473 TmpY := M.ReadLongInt();
2475 ReleaseKeys;
2477 if LongBool(kByte and NET_KEY_CHAT) then
2478 PressKey(KEY_CHAT, 10000)
2479 else
2480 begin
2481 if LongBool(kByte and NET_KEY_LEFT) then PressKey(KEY_LEFT, 10000);
2482 if LongBool(kByte and NET_KEY_RIGHT) then PressKey(KEY_RIGHT, 10000);
2483 if LongBool(kByte and NET_KEY_UP) then PressKey(KEY_UP, 10000);
2484 if LongBool(kByte and NET_KEY_DOWN) then PressKey(KEY_DOWN, 10000);
2485 if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
2486 end;
2488 JustTeleported := LongBool(kByte and NET_KEY_FORCEDIR);
2490 if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or JustTeleported then
2491 SetDirection(TDirection(Dir));
2493 GameVelX := M.ReadLongInt();
2494 GameVelY := M.ReadLongInt();
2495 GameAccelX := M.ReadLongInt();
2496 GameAccelY := M.ReadLongInt();
2497 SetLerp(TmpX, TmpY);
2498 if NetForcePlayerUpdate then Update();
2499 end;
2500 end;
2502 function MC_RECV_PlayerStats(var M: TMsg): Word;
2503 var
2504 PID: Word;
2505 Pl: TPlayer;
2506 I, OldFire: Integer;
2507 OldJet, Flam: Boolean;
2508 NewTeam: Byte;
2509 begin
2510 PID := M.ReadWord();
2511 Pl := g_Player_Get(PID);
2512 Result := 0;
2513 if Pl = nil then
2514 Exit;
2516 with Pl do
2517 begin
2518 alive := (M.ReadByte() <> 0);
2519 GodMode := (M.ReadByte() <> 0);
2520 Health := M.ReadLongInt();
2521 Armor := M.ReadLongInt();
2522 Air := M.ReadLongInt();
2523 JetFuel := M.ReadLongInt();
2524 Lives := M.ReadByte();
2525 NewTeam := M.ReadByte();
2527 for I := WP_FIRST to WP_LAST do
2528 FWeapon[I] := (M.ReadByte() <> 0);
2530 for I := A_BULLETS to A_HIGH do
2531 FAmmo[I] := M.ReadWord();
2533 for I := A_BULLETS to A_HIGH do
2534 FMaxAmmo[I] := M.ReadWord();
2536 for I := MR_SUIT to MR_MAX do
2537 FMegaRulez[I] := M.ReadLongWord();
2539 FRulez := [];
2540 if (M.ReadByte() <> 0) then
2541 FRulez := FRulez + [R_ITEM_BACKPACK];
2542 if (M.ReadByte() <> 0) then
2543 FRulez := FRulez + [R_KEY_RED];
2544 if (M.ReadByte() <> 0) then
2545 FRulez := FRulez + [R_KEY_GREEN];
2546 if (M.ReadByte() <> 0) then
2547 FRulez := FRulez + [R_KEY_BLUE];
2548 if (M.ReadByte() <> 0) then
2549 FRulez := FRulez + [R_BERSERK];
2551 Frags := M.ReadLongInt();
2552 Death := M.ReadLongInt();
2554 SetWeapon(M.ReadByte());
2556 FSpectator := M.ReadByte() <> 0;
2557 if FSpectator then
2558 begin
2559 if UID = NetPlrUID1 then
2560 begin
2561 gSpectLatchPID1 := UID;
2562 gPlayer1 := nil;
2563 end;
2564 if UID = NetPlrUID2 then
2565 begin
2566 gSpectLatchPID2 := UID;
2567 gPlayer2 := nil;
2568 end;
2569 end
2570 else
2571 begin
2572 if (gPlayer1 = nil) and (gSpectLatchPID1 > 0) and (UID = gSpectLatchPID1) then
2573 begin
2574 gPlayer1 := Pl;
2575 gSpectLatchPID1 := 0;
2576 end;
2577 if (gPlayer2 = nil) and (gSpectLatchPID2 > 0) and (UID = gSpectLatchPID2) then
2578 begin
2579 gPlayer2 := Pl;
2580 gSpectLatchPID2 := 0;
2581 end;
2582 end;
2583 FGhost := M.ReadByte() <> 0;
2584 FPhysics := M.ReadByte() <> 0;
2585 FNoRespawn := M.ReadByte() <> 0;
2586 OldJet := FJetpack;
2587 FJetpack := M.ReadByte() <> 0;
2588 OldFire := FFireTime;
2589 FFireTime := M.ReadLongInt();
2590 if (OldFire <= 0) and (FFireTime > 0) then
2591 g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y);
2592 Flam := M.ReadByte() <> 0;
2593 FSpawnInvul := M.ReadLongInt();
2594 if OldJet and not FJetpack then
2595 JetpackOff
2596 else if not OldJet and FJetpack then
2597 JetpackOn;
2598 if FFlaming and not Flam then
2599 FlamerOff;
2600 if Team <> NewTeam then
2601 Pl.ChangeTeam(NewTeam);
2602 end;
2604 Result := PID;
2605 end;
2607 function MC_RECV_PlayerDamage(var M: TMsg): Word;
2608 var
2609 PID: Word;
2610 Pl: TPlayer;
2611 Kind: Byte;
2612 Attacker, Value: Word;
2613 VX, VY: Integer;
2614 begin
2615 Result := 0;
2616 if not gGameOn then Exit;
2617 PID := M.ReadWord();
2618 Pl := g_Player_Get(PID);
2619 if Pl = nil then Exit;
2621 Kind := M.ReadByte();
2622 Attacker := M.ReadWord();
2623 Value := M.ReadWord();
2624 VX := M.ReadWord();
2625 VY := M.ReadWord();
2627 with Pl do
2628 Damage(Value, Attacker, VX, VY, Kind);
2630 Result := PID;
2631 end;
2633 function MC_RECV_PlayerDeath(var M: TMsg): Word;
2634 var
2635 PID: Word;
2636 Pl: TPlayer;
2637 KillType, DeathType: Byte;
2638 Attacker: Word;
2639 begin
2640 Result := 0;
2641 if not gGameOn then Exit;
2642 PID := M.ReadWord();
2643 Pl := g_Player_Get(PID);
2644 if Pl = nil then Exit;
2646 KillType := M.ReadByte();
2647 DeathType := M.ReadByte();
2648 Attacker := M.ReadWord();
2650 with Pl do
2651 begin
2652 Kill(KillType, Attacker, DeathType);
2653 SoftReset;
2654 end;
2655 end;
2657 function MC_RECV_PlayerDelete(var M: TMsg): Word;
2658 var
2659 PID: Word;
2660 Pl: TPlayer;
2661 begin
2662 PID := M.ReadWord();
2663 Pl := g_Player_Get(PID);
2664 Result := 0;
2665 if Pl = nil then Exit;
2667 if NetDeafLevel < 3 then
2668 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
2669 e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', TMsgType.Notify);
2671 g_Player_Remove(PID);
2673 Result := PID;
2674 end;
2676 function MC_RECV_PlayerFire(var M: TMsg): Word;
2677 var
2678 PID: Word;
2679 Weap: Byte;
2680 Pl: TPlayer;
2681 X, Y, AX, AY: Integer;
2682 SHID: Integer;
2683 begin
2684 Result := 0;
2685 if not gGameOn then Exit;
2686 PID := M.ReadWord();
2687 Pl := g_Player_Get(PID);
2688 if Pl = nil then Exit;
2690 Weap := M.ReadByte();
2691 X := M.ReadLongInt();
2692 Y := M.ReadLongInt();
2693 AX := M.ReadLongInt();
2694 AY := M.ReadLongInt();
2695 SHID := M.ReadLongInt();
2697 with Pl do
2698 if alive then NetFire(Weap, X, Y, AX, AY, SHID);
2699 end;
2701 procedure MC_RECV_PlayerSettings(var M: TMsg);
2702 var
2703 TmpName: string;
2704 TmpModel: string;
2705 CheckModel: string;
2706 TmpColor: TRGB;
2707 TmpTeam: Byte;
2708 Pl: TPlayer;
2709 PID: Word;
2710 begin
2711 PID := M.ReadWord();
2712 Pl := g_Player_Get(PID);
2713 if Pl = nil then Exit;
2715 TmpName := M.ReadString();
2716 TmpModel := M.ReadString();
2717 TmpColor.R := M.ReadByte();
2718 TmpColor.G := M.ReadByte();
2719 TmpColor.B := M.ReadByte();
2720 TmpTeam := M.ReadByte();
2722 if (gGameSettings.GameMode in [GM_TDM, GM_CTF]) and (Pl.Team <> TmpTeam) then
2723 begin
2724 Pl.ChangeTeam(TmpTeam);
2725 if gPlayer1 = Pl then
2726 gPlayer1Settings.Team := TmpTeam;
2727 if gPlayer2 = Pl then
2728 gPlayer2Settings.Team := TmpTeam;
2729 end else
2730 Pl.SetColor(TmpColor);
2732 if Pl.Name <> TmpName then
2733 begin
2734 if NetDeafLevel < 3 then
2735 g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
2736 Pl.Name := TmpName;
2737 end;
2739 if (g_Force_Model_Get() <> 0) then
2740 TmpModel := g_Forced_Model_GetName();
2741 if TmpModel <> Pl.Model.GetName() then
2742 Pl.SetModel(TmpModel);
2743 end;
2745 // ITEM
2747 procedure MC_RECV_ItemSpawn(var M: TMsg);
2748 var
2749 ID: Word;
2750 X, Y, VX, VY: Integer;
2751 T: Byte;
2752 Quiet, Fall{, Resp}: Boolean;
2753 it: PItem;
2754 begin
2755 if not gGameOn then Exit;
2756 ID := M.ReadWord();
2757 Quiet := M.ReadByte() <> 0;
2758 T := M.ReadByte();
2759 Fall := M.ReadByte() <> 0;
2760 {Resp :=} M.ReadByte();
2761 X := M.ReadLongInt();
2762 Y := M.ReadLongInt();
2763 VX := M.ReadLongInt();
2764 VY := M.ReadLongInt();
2766 g_Items_Create(X, Y, T and $7F, Fall, False, False, ID);
2767 if ((T and $80) <> 0) then g_Items_SetDrop(ID);
2769 it := g_Items_ByIdx(ID);
2770 it.Obj.Vel.X := VX;
2771 it.Obj.Vel.Y := VY;
2773 if not Quiet then
2774 begin
2775 g_Sound_PlayExAt('SOUND_ITEM_RESPAWNITEM', X, Y);
2776 {$IFDEF ENABLE_GFX}
2777 g_GFX_QueueEffect(R_GFX_ITEM_RESPAWN, X+(it.Obj.Rect.Width div 2)-16, Y+(it.Obj.Rect.Height div 2)-16);
2778 {$ENDIF}
2779 end;
2780 end;
2782 procedure MC_RECV_ItemDestroy(var M: TMsg);
2783 var
2784 ID: Word;
2785 Quiet: Boolean;
2786 begin
2787 if not gGameOn then Exit;
2788 ID := M.ReadWord();
2789 Quiet := M.ReadByte() <> 0;
2791 if not g_Items_ValidId(ID) then exit;
2793 if not Quiet then g_Items_EmitPickupSound(ID);
2795 g_Items_Remove(ID);
2796 end;
2798 procedure MC_RECV_ItemPos(var M: TMsg);
2799 var
2800 ID: Word;
2801 X, Y, VX, VY: Integer;
2802 it: PItem;
2803 begin
2804 if not gGameOn then Exit;
2806 ID := M.ReadWord();
2807 X := M.ReadLongInt();
2808 Y := M.ReadLongInt();
2809 VX := M.ReadLongInt();
2810 VY := M.ReadLongInt();
2812 if g_Items_ValidId(ID) then
2813 begin
2814 it := g_Items_ByIdx(ID);
2815 it.Obj.X := X;
2816 it.Obj.Y := Y;
2817 it.Obj.Vel.X := VX;
2818 it.Obj.Vel.Y := VY;
2819 it.positionChanged();
2820 end;
2821 end;
2823 // PANEL
2825 procedure MC_RECV_PanelTexture(var M: TMsg);
2826 var
2827 TP: TPanel;
2828 PGUID: Integer;
2829 Tex, Fr: Integer;
2830 Loop, Cnt: Byte;
2831 begin
2832 if not gGameOn then Exit;
2834 PGUID := Integer(M.ReadLongWord());
2835 Tex := M.ReadLongInt();
2836 Fr := M.ReadLongInt();
2837 Cnt := M.ReadByte();
2838 Loop := M.ReadByte();
2840 TP := g_Map_PanelByGUID(PGUID);
2841 if (TP <> nil) then
2842 begin
2843 // switch texture
2844 TP.SetTexture(Tex, Loop);
2845 TP.SetFrame(Fr, Cnt);
2846 end;
2847 end;
2849 procedure MC_RECV_PanelState(var M: TMsg);
2850 var
2851 PGUID: Integer;
2852 E: Boolean;
2853 Lift: Byte;
2854 X, Y, W, H: Integer;
2855 TP: TPanel;
2856 speedX, speedY, startX, startY, endX, endY: Integer;
2857 sizeSpX, sizeSpY, sizeEX, sizeEY: Integer;
2858 mpflags: Byte;
2859 begin
2860 if not gGameOn then Exit;
2862 PGUID := Integer(M.ReadLongWord());
2863 E := (M.ReadByte() <> 0);
2864 Lift := M.ReadByte();
2865 X := M.ReadLongInt();
2866 Y := M.ReadLongInt();
2867 W := M.ReadWord();
2868 H := M.ReadWord();
2869 // mplats
2870 speedX := M.ReadLongInt();
2871 speedY := M.ReadLongInt();
2872 startX := M.ReadLongInt();
2873 startY := M.ReadLongInt();
2874 endX := M.ReadLongInt();
2875 endY := M.ReadLongInt();
2876 sizeSpX := M.ReadLongInt();
2877 sizeSpY := M.ReadLongInt();
2878 sizeEX := M.ReadLongInt();
2879 sizeEY := M.ReadLongInt();
2880 mpflags := M.ReadByte(); // bit0: TP.movingActive; bit1: TP.moveOnce
2882 TP := g_Map_PanelByGUID(PGUID);
2883 if (TP = nil) then exit;
2885 // update lifts state
2886 if TP.isGLift then g_Map_SetLiftGUID(PGUID, Lift);
2888 // update enabled/disabled state for all panels
2889 if E then g_Map_EnableWallGUID(PGUID) else g_Map_DisableWallGUID(PGUID);
2891 // update panel position, as it can be moved (mplat)
2892 TP.X := X;
2893 TP.Y := Y;
2894 TP.Width := W;
2895 TP.Height := H;
2896 // update mplat state
2897 TP.movingSpeedX := speedX;
2898 TP.movingSpeedY := speedY;
2899 TP.movingStartX := startX;
2900 TP.movingStartY := startY;
2901 TP.movingEndX := endX;
2902 TP.movingEndY := endY;
2903 TP.sizeSpeedX := sizeSpX;
2904 TP.sizeSpeedY := sizeSpY;
2905 TP.sizeEndX := sizeEX;
2906 TP.sizeEndY := sizeEY;
2907 TP.movingActive := ((mpflags and 1) <> 0);
2908 TP.moveOnce := ((mpflags and 2) <> 0);
2909 // notify panel of it's position/size change, so it can fix other internal structures
2910 TP.positionChanged();
2911 end;
2913 // TRIGGERS
2915 procedure MC_RECV_TriggerSound(var M: TMsg);
2916 var
2917 SPlaying: Boolean;
2918 SPos, SID: LongWord;
2919 SCount: LongInt;
2920 I: Integer;
2921 begin
2922 if not gGameOn then Exit;
2923 if gTriggers = nil then Exit;
2925 SID := M.ReadLongWord();
2926 SPlaying := M.ReadByte() <> 0;
2927 SPos := M.ReadLongWord();
2928 SCount := M.ReadLongInt();
2930 for I := Low(gTriggers) to High(gTriggers) do
2931 if gTriggers[I].TriggerType = TRIGGER_SOUND then
2932 if gTriggers[I].ClientID = SID then
2933 with gTriggers[I] do
2934 begin
2935 if Sound <> nil then
2936 begin
2937 if SPlaying then
2938 begin
2939 if tgcLocal then
2940 Sound.PlayVolumeAt(X+(Width div 2), Y+(Height div 2), tgcVolume/255.0)
2941 else
2942 Sound.PlayPanVolume((tgcPan-127.0)/128.0, tgcVolume/255.0);
2943 Sound.SetPosition(SPos);
2944 end
2945 else
2946 if Sound.IsPlaying then Sound.Stop;
2947 end;
2949 SoundPlayCount := SCount;
2950 end;
2951 end;
2953 procedure MC_RECV_TriggerMusic(var M: TMsg);
2954 var
2955 MName: string;
2956 MPlaying: Boolean;
2957 MPos: LongWord;
2958 MPaused: Boolean;
2959 begin
2960 if not gGameOn then Exit;
2962 MName := M.ReadString();
2963 MPlaying := M.ReadByte() <> 0;
2964 MPos := M.ReadLongWord();
2965 MPaused := M.ReadByte() <> 0;
2966 MPos := MPos+1; //k8: stfu, fpc!
2968 if MPlaying then
2969 begin
2970 gMusic.SetByName(MName);
2971 gMusic.Play(True);
2972 // gMusic.SetPosition(MPos);
2973 gMusic.SpecPause := MPaused;
2974 end
2975 else
2976 if gMusic.IsPlaying then gMusic.Stop;
2977 end;
2979 // MONSTERS
2981 procedure MC_RECV_MonsterSpawn(var M: TMsg);
2982 var
2983 ID: Word;
2984 MType, MState, MDir, MAnim, MBehav: Byte;
2985 X, Y, VX, VY, MTargTime, MHealth, MAmmo, MSleep: Integer;
2986 MTarg: Word;
2987 Mon: TMonster;
2988 begin
2989 ID := M.ReadWord();
2990 Mon := g_Monsters_ByUID(ID);
2991 if Mon <> nil then
2992 Exit;
2994 MType := M.ReadByte();
2995 MState := M.ReadByte();
2996 MAnim := M.ReadByte();
2997 MTarg := M.ReadWord();
2998 MTargTime := M.ReadLongInt();
2999 MBehav := M.ReadByte();
3000 MSleep := M.ReadLongInt();
3001 MHealth := M.ReadLongInt();
3002 MAmmo := M.ReadLongInt();
3004 X := M.ReadLongInt();
3005 Y := M.ReadLongInt();
3006 VX := M.ReadLongInt();
3007 VY := M.ReadLongInt();
3008 MDir := M.ReadByte();
3010 g_Monsters_Create(MType, X, Y, TDirection(MDir), False, ID);
3011 Mon := g_Monsters_ByUID(ID);
3012 if Mon = nil then
3013 Exit;
3015 with Mon do
3016 begin
3018 MonsterAnim := MAnim;
3019 MonsterTargetUID := MTarg;
3020 MonsterTargetTime := MTargTime;
3021 MonsterBehaviour := MBehav;
3022 MonsterSleep := MSleep;
3023 MonsterAmmo := MAmmo;
3024 SetHealth(MHealth);
3026 SetState(MState);
3028 setPosition(X, Y); // this will call positionChanged();
3029 GameVelX := VX;
3030 GameVelY := VY;
3031 end;
3032 end;
3034 procedure MC_RECV_MonsterPos(var M: TMsg);
3035 var
3036 Mon: TMonster;
3037 ID: Word;
3038 X, Y: Integer;
3039 begin
3040 ID := M.ReadWord();
3041 Mon := g_Monsters_ByUID(ID);
3042 if Mon = nil then
3043 Exit;
3045 with Mon do
3046 begin
3047 X := M.ReadLongInt();
3048 Y := M.ReadLongInt();
3049 Mon.setPosition(X, Y); // this will call `positionChanged()`
3050 GameVelX := M.ReadLongInt();
3051 GameVelY := M.ReadLongInt();
3052 GameDirection := TDirection(M.ReadByte());
3053 end;
3054 end;
3056 procedure MC_RECV_MonsterState(var M: TMsg);
3057 var
3058 ID, OldFire: Integer;
3059 MState, MFAnm: Byte;
3060 Mon: TMonster;
3061 AnimRevert: Boolean;
3062 begin
3063 ID := M.ReadWord();
3064 Mon := g_Monsters_ByUID(ID);
3065 if Mon = nil then Exit;
3067 MState := M.ReadByte();
3068 MFAnm := M.ReadByte();
3070 with Mon do
3071 begin
3072 MonsterTargetUID := M.ReadWord();
3073 MonsterTargetTime := M.ReadLongInt();
3074 MonsterSleep := M.ReadLongInt();
3075 MonsterHealth := M.ReadLongInt();
3076 MonsterAmmo := M.ReadLongInt();
3077 MonsterPain := M.ReadLongInt();
3078 AnimRevert := M.ReadByte() <> 0;
3079 OldFire := FFireTime;
3080 FFireTime := M.ReadLongInt();
3081 if (OldFire <= 0) and (FFireTime > 0) then
3082 g_Sound_PlayExAt('SOUND_IGNITE', Obj.X, Obj.Y);
3083 RevertAnim(AnimRevert);
3085 if MonsterState <> MState then
3086 begin
3087 if (MState = MONSTATE_GO) and (MonsterState = MONSTATE_SLEEP) then WakeUpSound();
3088 if (MState = MONSTATE_DIE) then DieSound();
3089 if (MState = MONSTATE_PAIN) then MakeBloodSimple(Min(200, MonsterPain));
3090 if (MState = MONSTATE_ATTACK) then kick(nil);
3091 if (MState = MONSTATE_DEAD) then SetDeadAnim();
3093 SetState(MState, MFAnm);
3094 end;
3095 end;
3096 end;
3098 procedure MC_RECV_MonsterShot(var M: TMsg);
3099 var
3100 ID: Integer;
3101 Mon: TMonster;
3102 X, Y, VX, VY: Integer;
3103 begin
3104 ID := M.ReadWord();
3106 Mon := g_Monsters_ByUID(ID);
3107 if Mon = nil then Exit;
3109 X := M.ReadLongInt();
3110 Y := M.ReadLongInt();
3111 VX := M.ReadLongInt();
3112 VY := M.ReadLongInt();
3114 Mon.ClientAttack(X, Y, VX, VY);
3115 end;
3117 procedure MC_RECV_MonsterDelete(var M: TMsg);
3118 var
3119 ID: Integer;
3120 Mon: TMonster;
3121 begin
3122 ID := M.ReadWord();
3123 Mon := g_Monsters_ByUID(ID);
3124 if Mon = nil then Exit;
3125 Mon.SetState(5);
3126 Mon.MonsterRemoved := True;
3127 end;
3129 procedure MC_RECV_TimeSync(var M: TMsg);
3130 var
3131 Time: LongWord;
3132 begin
3133 Time := M.ReadLongWord();
3135 if gState = STATE_INTERCUSTOM then
3136 gServInterTime := Min(Time, 255);
3137 end;
3139 procedure MC_RECV_VoteEvent(var M: TMsg);
3140 var
3141 EvID: Byte;
3142 Str1, Str2: string;
3143 Int1, Int2: SmallInt;
3144 begin
3145 EvID := M.ReadByte();
3146 Int1 := M.ReadSmallInt();
3147 Int2 := M.ReadSmallInt();
3148 Str1 := M.ReadString();
3149 Str2 := M.ReadString();
3151 if NetDeafLevel < 2 then
3152 case EvID of
3153 NET_VE_STARTED:
3154 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True);
3155 NET_VE_PASSED:
3156 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True);
3157 NET_VE_FAILED:
3158 g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
3159 NET_VE_VOTE:
3160 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True);
3161 NET_VE_INPROGRESS:
3162 g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True);
3163 end;
3164 end;
3166 // CLIENT SEND
3168 procedure MC_SEND_Info(Password: string);
3169 var i: Integer;
3170 begin
3171 NetOut.Clear();
3173 NetOut.Write(Byte(NET_MSG_INFO));
3174 NetOut.Write(GAME_VERSION);
3175 NetOut.Write(Password);
3176 NetOut.Write(gPlayer1Settings.Name);
3177 NetOut.Write(gPlayer1Settings.Model);
3178 NetOut.Write(gPlayer1Settings.Color.R);
3179 NetOut.Write(gPlayer1Settings.Color.G);
3180 NetOut.Write(gPlayer1Settings.Color.B);
3181 NetOut.Write(gPlayer1Settings.Team);
3182 NetOut.Write(gPlayer1Settings.WeaponSwitch);
3183 for i := WP_FIRST to WP_LAST + 1 do
3184 NetOut.Write(gPlayer1Settings.WeaponPreferences[i]);
3185 NetOut.Write(gPlayer1Settings.SwitchToEmpty);
3186 NetOut.Write(gPlayer1Settings.SkipFist);
3188 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3189 end;
3191 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
3192 begin
3193 NetOut.Write(Byte(NET_MSG_CHAT));
3194 NetOut.Write(Txt);
3195 NetOut.Write(Mode);
3197 g_Net_Client_Send(True, NET_CHAN_CHAT);
3198 end;
3200 procedure MC_SEND_PlayerPos();
3201 var
3202 kByte: Word;
3203 Predict: Boolean;
3204 strafeDir: Byte;
3205 WeaponAct: Byte = 0;
3206 WeaponSelect: Word = 0;
3207 i: Integer;
3208 begin
3209 if not gGameOn then Exit;
3210 if gPlayers = nil then Exit;
3211 if gPlayer1 = nil then Exit;
3213 kByte := 0;
3214 Predict := NetPredictSelf; // and (not NetGotKeys);
3216 {$IFDEF DISABLE_MENU}
3217 if (not gConsoleShow) and (not gChatShow) then
3218 {$ELSE}
3219 if (not gConsoleShow) and (not gChatShow) and (g_ActiveWindow = nil) then
3220 {$ENDIF}
3221 begin
3222 strafeDir := P1MoveButton shr 4;
3223 P1MoveButton := P1MoveButton and $0F;
3225 if gPlayerAction[0, ACTION_MOVELEFT] and (not gPlayerAction[0, ACTION_MOVERIGHT]) then
3226 P1MoveButton := 1
3227 else if (not gPlayerAction[0, ACTION_MOVELEFT]) and gPlayerAction[0, ACTION_MOVERIGHT] then
3228 P1MoveButton := 2
3229 else if (not gPlayerAction[0, ACTION_MOVELEFT]) and (not gPlayerAction[0, ACTION_MOVERIGHT]) then
3230 P1MoveButton := 0;
3232 // strafing
3233 if gPlayerAction[0, ACTION_STRAFE] then
3234 begin
3235 // new strafe mechanics
3236 if (strafeDir = 0) then
3237 strafeDir := P1MoveButton; // start strafing
3238 // now set direction according to strafe (reversed)
3239 if (strafeDir = 2) then
3240 gPlayer1.SetDirection(TDirection.D_LEFT)
3241 else if (strafeDir = 1) then
3242 gPlayer1.SetDirection(TDirection.D_RIGHT)
3243 end
3244 else
3245 begin
3246 strafeDir := 0; // not strafing anymore
3247 if (P1MoveButton = 2) and gPlayerAction[0, ACTION_MOVELEFT] then
3248 gPlayer1.SetDirection(TDirection.D_LEFT)
3249 else if (P1MoveButton = 1) and gPlayerAction[0, ACTION_MOVERIGHT] then
3250 gPlayer1.SetDirection(TDirection.D_RIGHT)
3251 else if P1MoveButton <> 0 then
3252 gPlayer1.SetDirection(TDirection(P1MoveButton-1));
3253 end;
3255 gPlayer1.ReleaseKeys;
3256 if P1MoveButton = 1 then
3257 begin
3258 kByte := kByte or NET_KEY_LEFT;
3259 if Predict then gPlayer1.PressKey(KEY_LEFT, 10000);
3260 end;
3261 if P1MoveButton = 2 then
3262 begin
3263 kByte := kByte or NET_KEY_RIGHT;
3264 if Predict then gPlayer1.PressKey(KEY_RIGHT, 10000);
3265 end;
3266 if gPlayerAction[0, ACTION_LOOKUP] then
3267 begin
3268 kByte := kByte or NET_KEY_UP;
3269 gPlayer1.PressKey(KEY_UP, 10000);
3270 end;
3271 if gPlayerAction[0, ACTION_LOOKDOWN] then
3272 begin
3273 kByte := kByte or NET_KEY_DOWN;
3274 gPlayer1.PressKey(KEY_DOWN, 10000);
3275 end;
3276 if gPlayerAction[0, ACTION_JUMP] then
3277 begin
3278 kByte := kByte or NET_KEY_JUMP;
3279 // gPlayer1.PressKey(KEY_JUMP, 10000); // TODO: Make a prediction option
3280 end;
3281 if gPlayerAction[0, ACTION_ATTACK] then kByte := kByte or NET_KEY_FIRE;
3282 if gPlayerAction[0, ACTION_ACTIVATE] then kByte := kByte or NET_KEY_OPEN;
3284 for i := WP_FACT to WP_LACT do
3285 begin
3286 if gWeaponAction[0, i] then
3287 begin
3288 WeaponAct := WeaponAct or Byte(1 shl i);
3289 gWeaponAction[0, i] := False
3290 end
3291 end;
3293 for i := WP_FIRST to WP_LAST do
3294 begin
3295 if gSelectWeapon[0, i] then
3296 begin
3297 WeaponSelect := WeaponSelect or Word(1 shl i);
3298 gSelectWeapon[0, i] := False
3299 end
3300 end;
3302 // fix movebutton state
3303 P1MoveButton := P1MoveButton or (strafeDir shl 4);
3304 end
3305 else
3306 kByte := NET_KEY_CHAT;
3308 NetOut.Write(Byte(NET_MSG_PLRPOS));
3309 NetOut.Write(gTime);
3310 NetOut.Write(kByte);
3311 NetOut.Write(Byte(gPlayer1.Direction));
3312 NetOut.Write(WeaponAct);
3313 NetOut.Write(WeaponSelect);
3314 //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING);
3315 g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
3317 //kBytePrev := kByte;
3318 //kDirPrev := gPlayer1.Direction;
3319 end;
3321 procedure MC_SEND_Vote(Start: Boolean = False; Command: string = 'a');
3322 begin
3323 NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
3324 NetOut.Write(Byte(Start));
3325 NetOut.Write(Command);
3326 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3327 end;
3329 procedure MC_SEND_PlayerSettings();
3330 var i: Integer;
3331 begin
3332 NetOut.Write(Byte(NET_MSG_PLRSET));
3333 NetOut.Write(gPlayer1Settings.Name);
3334 NetOut.Write(gPlayer1Settings.Model);
3335 NetOut.Write(gPlayer1Settings.Color.R);
3336 NetOut.Write(gPlayer1Settings.Color.G);
3337 NetOut.Write(gPlayer1Settings.Color.B);
3338 NetOut.Write(gPlayer1Settings.Team);
3339 NetOut.Write(gPlayer1Settings.WeaponSwitch);
3340 for i := WP_FIRST to WP_LAST + 1 do
3341 NetOut.Write(gPlayer1Settings.WeaponPreferences[i]);
3342 NetOut.Write(gPlayer1Settings.SwitchToEmpty);
3343 NetOut.Write(gPlayer1Settings.SkipFist);
3345 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3346 end;
3348 procedure MC_SEND_FullStateRequest();
3349 begin
3350 NetOut.Write(Byte(NET_MSG_REQFST));
3352 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3353 end;
3355 procedure MC_SEND_CheatRequest(Kind: Byte);
3356 begin
3357 NetOut.Write(Byte(NET_MSG_CHEAT));
3358 NetOut.Write(Kind);
3360 g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
3361 end;
3362 procedure MC_SEND_RCONPassword(Password: string);
3363 begin
3364 NetOut.Write(Byte(NET_MSG_RCON_AUTH));
3365 NetOut.Write(Password);
3367 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3368 end;
3369 procedure MC_SEND_RCONCommand(Cmd: string);
3370 begin
3371 NetOut.Write(Byte(NET_MSG_RCON_CMD));
3372 NetOut.Write(Cmd);
3374 g_Net_Client_Send(True, NET_CHAN_SERVICE);
3375 end;
3378 end.