hedgewars/uIO.pas
branchsdl2transition
changeset 11362 ed5a6478e710
parent 9682 aa2431ed87b2
parent 11046 47a8c19ecb60
child 11363 9006e158a81f
equal deleted inserted replaced
11361:31570b766315 11362:ed5a6478e710
     1 (*
     1 (*
     2  * Hedgewars, a free turn based strategy game
     2  * Hedgewars, a free turn based strategy game
     3  * Copyright (c) 2004-2013 Andrey Korotaev <unC0Rr@gmail.com>
     3  * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
     4  *
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation; version 2 of the License
     7  * the Free Software Foundation; version 2 of the License
     8  *
     8  *
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    12  * GNU General Public License for more details.
    13  *
    13  *
    14  * You should have received a copy of the GNU General Public License
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program; if not, write to the Free Software
    15  * along with this program; if not, write to the Free Software
    16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
    16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    17  *)
    17  *)
    18 
    18 
    19 {$INCLUDE "options.inc"}
    19 {$INCLUDE "options.inc"}
    20 
    20 
    21 unit uIO;
    21 unit uIO;
    49      TCmd = packed record
    49      TCmd = packed record
    50             Next: PCmd;
    50             Next: PCmd;
    51             loTime: Word;
    51             loTime: Word;
    52             case byte of
    52             case byte of
    53             1: (len: byte;
    53             1: (len: byte;
    54                 cmd: Char;
    54                 cmd: Char);
    55                 X, Y: LongInt);
       
    56             2: (str: shortstring);
    55             2: (str: shortstring);
    57             end;
    56             end;
    58 
    57 
    59 var IPCSock: PTCPSocket;
    58 var IPCSock: PTCPSocket;
    60     fds: PSDLNet_SocketSet;
    59     fds: PSDLNet_SocketSet;
    75 begin
    74 begin
    76 new(command);
    75 new(command);
    77 FillChar(command^, sizeof(TCmd), 0);
    76 FillChar(command^, sizeof(TCmd), 0);
    78 command^.loTime:= Time;
    77 command^.loTime:= Time;
    79 command^.str:= str;
    78 command^.str:= str;
    80 if command^.cmd <> 'F' then dec(command^.len, 2); // cut timestamp
    79 if (command^.cmd <> 'F') and (command^.cmd <> 'G') then dec(command^.len, 2); // cut timestamp
    81 if headcmd = nil then
    80 if headcmd = nil then
    82     begin
    81     begin
    83     headcmd:= command;
    82     headcmd:= command;
    84     lastcmd:= command
    83     lastcmd:= command
    85     end
    84     end
   117     IPCSock:= SDLNet_TCP_Open(ipaddr);
   116     IPCSock:= SDLNet_TCP_Open(ipaddr);
   118     SDLTry(IPCSock <> nil, 'SDLNet_TCP_Open', true);
   117     SDLTry(IPCSock <> nil, 'SDLNet_TCP_Open', true);
   119     WriteLnToConsole(msgOK)
   118     WriteLnToConsole(msgOK)
   120 end;
   119 end;
   121 
   120 
       
   121 procedure ParseChatCommand(command: shortstring; message: shortstring;
       
   122                            messageStartIndex: Byte);
       
   123 var
       
   124     text: shortstring;
       
   125 begin
       
   126     text:= copy(message, messageStartIndex,
       
   127                 Length(message) - messageStartIndex + 1);
       
   128     ParseCommand(command + text, true);
       
   129     WriteLnToConsole(text)
       
   130 end;
       
   131 
   122 procedure ParseIPCCommand(s: shortstring);
   132 procedure ParseIPCCommand(s: shortstring);
   123 var loTicks: Word;
   133 var loTicks: Word;
   124 begin
   134     isProcessed: boolean;
       
   135 begin
       
   136 isProcessed := true;
       
   137 
   125 case s[1] of
   138 case s[1] of
   126      '!': begin AddFileLog('Ping? Pong!'); isPonged:= true; end;
   139      '!': begin AddFileLog('Ping? Pong!'); isPonged:= true; end;
   127      '?': SendIPC(_S'!');
   140      '?': SendIPC(_S'!');
   128      'e': ParseCommand(copy(s, 2, Length(s) - 1), true);
   141      'e': ParseCommand(copy(s, 2, Length(s) - 1), true);
   129      'E': OutError(copy(s, 2, Length(s) - 1), true);
   142      'E': OutError(copy(s, 2, Length(s) - 1), true);
   138                'V': GameType:= gmtRecord;
   151                'V': GameType:= gmtRecord;
   139                else OutError(errmsgIncorrectUse + ' IPC "T" :' + s[2], true) end;
   152                else OutError(errmsgIncorrectUse + ' IPC "T" :' + s[2], true) end;
   140      'V': begin
   153      'V': begin
   141               if s[2] = '.' then
   154               if s[2] = '.' then
   142                   ParseCommand('campvar ' + copy(s, 3, length(s) - 2), true);
   155                   ParseCommand('campvar ' + copy(s, 3, length(s) - 2), true);
   143           end
   156           end;
       
   157      'I': ParseCommand('pause server', true);
       
   158      's': if gameType = gmtNet then
       
   159              ParseChatCommand('chatmsg ', s, 2)
       
   160           else
       
   161              isProcessed:= false;
       
   162      'b': if gameType = gmtNet then
       
   163              ParseChatCommand('chatmsg ' + #4, s, 2)
       
   164           else
       
   165              isProcessed:= false;
       
   166      'Y': ChatPasteBuffer:= copy(s, 2, Length(s) - 1);
   144      else
   167      else
   145      loTicks:= SDLNet_Read16(@s[byte(s[0]) - 1]);
   168         isProcessed:= false;
   146      AddCmd(loTicks, s);
   169      end;
   147      AddFileLog('[IPC in] ' + sanitizeCharForLog(s[1]) + ' ticks ' + IntToStr(lastcmd^.loTime));
   170 
   148      end
   171     if (not isProcessed) then
       
   172     begin
       
   173         loTicks:= SDLNet_Read16(@s[byte(s[0]) - 1]);
       
   174         AddCmd(loTicks, s);
       
   175         AddFileLog('[IPC in] ' + sanitizeCharForLog(s[1]) + ' ticks ' + IntToStr(lastcmd^.loTime));
       
   176     end
   149 end;
   177 end;
   150 
   178 
   151 procedure IPCCheckSock;
   179 procedure IPCCheckSock;
   152 var i: LongInt;
   180 var i: LongInt;
   153     s: shortstring;
   181     s: shortstring;
   175         OutError('IPC connection lost', true)
   203         OutError('IPC connection lost', true)
   176     end;
   204     end;
   177 end;
   205 end;
   178 
   206 
   179 procedure LoadRecordFromFile(fileName: shortstring);
   207 procedure LoadRecordFromFile(fileName: shortstring);
   180 var f: file;
   208 var f  : File;
   181     ss: shortstring = '';
   209     ss : shortstring = '';
   182     i: LongInt;
   210     i  : LongInt;
   183     s: shortstring;
   211     s  : shortstring;
   184 begin
   212 begin
   185 
   213 
   186 // set RDNLY on file open
   214 // set RDNLY on file open
   187 filemode:= 0;
   215 filemode:= 0;
   188 {$I-}
   216 {$I-}
   189 assign(f, fileName);
   217 assign(f, fileName);
   190 reset(f, 1);
   218 reset(f, 1);
   191 
       
   192 tryDo(IOResult = 0, 'Error opening file ' + fileName, true);
   219 tryDo(IOResult = 0, 'Error opening file ' + fileName, true);
   193 
   220 
   194 i:= 0; // avoid compiler hints
   221 i:= 0; // avoid compiler hints
   195 s[0]:= #0;
   222 s[0]:= #0;
   196 repeat
   223 repeat
   200         s[0]:= char(i);
   227         s[0]:= char(i);
   201         ss:= ss + s;
   228         ss:= ss + s;
   202         while (Length(ss) > 1)and(Length(ss) > byte(ss[1])) do
   229         while (Length(ss) > 1)and(Length(ss) > byte(ss[1])) do
   203             begin
   230             begin
   204             ParseIPCCommand(copy(ss, 2, byte(ss[1])));
   231             ParseIPCCommand(copy(ss, 2, byte(ss[1])));
   205             Delete(ss, 1, Succ(byte(ss[1])))
   232             Delete(ss, 1, Succ(byte(ss[1])));
   206             end
   233             end
   207         end
   234         end
   208 until i = 0;
   235 until i = 0;
   209 
   236 
   210 close(f)
   237 close(f)
   219 SendIPCRaw(@buf[0], length(buf) + 1)
   246 SendIPCRaw(@buf[0], length(buf) + 1)
   220 end;
   247 end;
   221 
   248 
   222 function isSyncedCommand(c: char): boolean;
   249 function isSyncedCommand(c: char): boolean;
   223 begin
   250 begin
   224     isSyncedCommand:= (c in ['+', '#', 'L', 'l', 'R', 'r', 'U', 'u', 'D', 'd', 'Z', 'z', 'A', 'a', 'S', 'j', 'J', ',', 'c', 'N', 'p', 'P', 'w', 't', '1', '2', '3', '4', '5']) or ((c >= #128) and (c <= char(128 + cMaxSlotIndex)))
   251     case c of
       
   252     '+', '#', 'L', 'l', 'R', 'r', 'U'
       
   253     , 'u', 'D', 'd', 'Z', 'z', 'A', 'a'
       
   254     , 'S', 'j', 'J', ',', 'c', 'N', 'p'
       
   255     , 'P', 'w', 't', '1', '2', '3', '4'
       
   256     , '5', 'f', 'g': isSyncedCommand:= true;
       
   257     else
       
   258         isSyncedCommand:= ((byte(c) >= 128) and (byte(c) <= 128 + cMaxSlotIndex))
       
   259     end
   225 end;
   260 end;
   226 
   261 
   227 procedure flushBuffer();
   262 procedure flushBuffer();
   228 begin
   263 begin
   229     if IPCSock <> nil then
   264     if IPCSock <> nil then
   238 begin
   273 begin
   239 if IPCSock <> nil then
   274 if IPCSock <> nil then
   240     begin
   275     begin
   241     if s[0] > #251 then
   276     if s[0] > #251 then
   242         s[0]:= #251;
   277         s[0]:= #251;
   243         
   278 
   244     SDLNet_Write16(GameTicks, @s[Succ(byte(s[0]))]);
   279     SDLNet_Write16(GameTicks, @s[Succ(byte(s[0]))]);
   245     
   280 
   246     AddFileLog('[IPC out] '+ sanitizeCharForLog(s[1]));
   281     AddFileLog('[IPC out] '+ sanitizeCharForLog(s[1]));
   247     inc(s[0], 2);
   282     inc(s[0], 2);
   248     
   283 
   249     if isSyncedCommand(s[1]) then
   284     if isSyncedCommand(s[1]) then
   250         begin
   285         begin
   251         if sendBuffer.count + byte(s[0]) >= cSendBufferSize then
   286         if sendBuffer.count + byte(s[0]) >= cSendBufferSize then
   252             flushBuffer();
   287             flushBuffer();
   253             
   288 
   254         Move(s, sendBuffer.buf[sendBuffer.count], byte(s[0]) + 1);
   289         Move(s, sendBuffer.buf[sendBuffer.count], byte(s[0]) + 1);
   255         inc(sendBuffer.count, byte(s[0]) + 1);
   290         inc(sendBuffer.count, byte(s[0]) + 1);
   256         
   291 
   257         if (s[1] = 'N') or (s[1] = '#') then
   292         if (s[1] = 'N') or (s[1] = '#') then
   258             flushBuffer();
   293             flushBuffer();
   259         end else
   294         end else
   260         SDLNet_TCP_Send(IPCSock, @s, Succ(byte(s[0])))
   295         SDLNet_TCP_Send(IPCSock, @s, Succ(byte(s[0])))
   261     end
   296     end
   300 inc(flushDelayTicks, Lag);
   335 inc(flushDelayTicks, Lag);
   301 if (flushDelayTicks >= cSendEmptyPacketTime) then
   336 if (flushDelayTicks >= cSendEmptyPacketTime) then
   302     begin
   337     begin
   303     if sendBuffer.count = 0 then
   338     if sendBuffer.count = 0 then
   304         SendIPC(_S'+');
   339         SendIPC(_S'+');
   305         
   340 
   306      flushBuffer()    
   341      flushBuffer()
   307     end
   342     end
   308 end;
   343 end;
   309 
   344 
   310 procedure NetGetNextCmd;
   345 procedure NetGetNextCmd;
   311 var tmpflag: boolean;
   346 var tmpflag: boolean;
   314 begin
   349 begin
   315 tmpflag:= true;
   350 tmpflag:= true;
   316 
   351 
   317 while (headcmd <> nil)
   352 while (headcmd <> nil)
   318     and (tmpflag or (headcmd^.cmd = '#')) // '#' is the only cmd which can be sent within same tick after 'N'
   353     and (tmpflag or (headcmd^.cmd = '#')) // '#' is the only cmd which can be sent within same tick after 'N'
   319     and ((GameTicks = hiTicks shl 16 + headcmd^.loTime)
   354     and ((GameTicks = LongWord(hiTicks shl 16 + headcmd^.loTime))
   320         or (headcmd^.cmd = 's') // for these commands time is not specified
   355         or (headcmd^.cmd = 's') // for these commands time is not specified
   321         or (headcmd^.cmd = 'h') // seems the hedgewars protocol does not allow remote synced commands
   356         or (headcmd^.cmd = 'h') // seems the hedgewars protocol does not allow remote synced commands
   322         or (headcmd^.cmd = '#') // must be synced for saves to work
   357         or (headcmd^.cmd = '#') // must be synced for saves to work
   323         or (headcmd^.cmd = 'b')
   358         or (headcmd^.cmd = 'b')
   324         or (headcmd^.cmd = 'F')) do
   359         or (headcmd^.cmd = 'F')
       
   360         or (headcmd^.cmd = 'G')) do
   325     begin
   361     begin
   326     case headcmd^.cmd of
   362     case headcmd^.cmd of
   327         '+': ; // do nothing - it is just an empty packet
   363         '+': ; // do nothing - it is just an empty packet
   328         '#': begin
   364         '#': begin
   329             AddFileLog('hiTicks increment by remote message');
   365             AddFileLog('hiTicks increment by remote message');
   347         ',': ParseCommand('skip', true);
   383         ',': ParseCommand('skip', true);
   348         'c': begin
   384         'c': begin
   349             s:= copy(headcmd^.str, 2, Pred(headcmd^.len));
   385             s:= copy(headcmd^.str, 2, Pred(headcmd^.len));
   350             ParseCommand('gencmd ' + s, true);
   386             ParseCommand('gencmd ' + s, true);
   351              end;
   387              end;
   352         's': begin
   388         's': ParseChatCommand('chatmsg ', headcmd^.str, 2);
   353             s:= copy(headcmd^.str, 2, Pred(headcmd^.len));
   389         'b': ParseChatCommand('chatmsg ' + #4, headcmd^.str, 2);
   354             ParseCommand('chatmsg ' + s, true);
   390         'F': ParseCommand('teamgone u' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true);
   355             WriteLnToConsole(s)
   391         'G': ParseCommand('teamback u' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true);
   356              end;
   392         'f': ParseCommand('teamgone s' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true);
   357         'b': begin
   393         'g': ParseCommand('teamback s' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true);
   358             s:= copy(headcmd^.str, 2, Pred(headcmd^.len));
       
   359             ParseCommand('chatmsg ' + #4 + s, true);
       
   360             WriteLnToConsole(s)
       
   361              end;
       
   362 // TODO: deprecate 'F'
       
   363         'F': ParseCommand('teamgone ' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true);
       
   364         'N': begin
   394         'N': begin
   365             tmpflag:= false;
   395             tmpflag:= false;
   366             lastTurnChecksum:= SDLNet_Read32(@headcmd^.str[2]);
   396             lastTurnChecksum:= SDLNet_Read32(@headcmd^.str[2]);
   367             AddFileLog('got cmd "N": time '+IntToStr(hiTicks shl 16 + headcmd^.loTime))
   397             AddFileLog('got cmd "N": time '+IntToStr(hiTicks shl 16 + headcmd^.loTime))
   368              end;
   398              end;
   369         'p': begin
   399         'p': begin
   370             x32:= SDLNet_Read32(@(headcmd^.X));
   400             x32:= SDLNet_Read32(@(headcmd^.str[2]));
   371             y32:= SDLNet_Read32(@(headcmd^.Y));
   401             y32:= SDLNet_Read32(@(headcmd^.str[6]));
   372             doPut(x32, y32, false)
   402             doPut(x32, y32, false)
   373              end;
   403              end;
   374         'P': begin
   404         'P': begin
   375             // these are equations solved for CursorPoint
   405             // these are equations solved for CursorPoint
   376             // SDLNet_Read16(@(headcmd^.X)) == CursorPoint.X - WorldDx;
   406             // SDLNet_Read16(@(headcmd^.X)) == CursorPoint.X - WorldDx;
   377             // SDLNet_Read16(@(headcmd^.Y)) == cScreenHeight - CursorPoint.Y - WorldDy;
   407             // SDLNet_Read16(@(headcmd^.Y)) == cScreenHeight - CursorPoint.Y - WorldDy;
   378             if CurrentTeam^.ExtDriven then
   408             if CurrentTeam^.ExtDriven then
   379                begin
   409                begin
   380                TargetCursorPoint.X:= LongInt(SDLNet_Read32(@(headcmd^.X))) + WorldDx;
   410                TargetCursorPoint.X:= LongInt(SDLNet_Read32(@(headcmd^.str[2]))) + WorldDx;
   381                TargetCursorPoint.Y:= cScreenHeight - LongInt(SDLNet_Read32(@(headcmd^.Y))) - WorldDy;
   411                TargetCursorPoint.Y:= cScreenHeight - LongInt(SDLNet_Read32(@(headcmd^.str[6]))) - WorldDy;
   382                if not bShowAmmoMenu and autoCameraOn then
   412                if not bShowAmmoMenu and autoCameraOn then
   383                     CursorPoint:= TargetCursorPoint
   413                     CursorPoint:= TargetCursorPoint
   384                end
   414                end
   385              end;
   415              end;
   386         'w': ParseCommand('setweap ' + headcmd^.str[2], true);
   416         'w': ParseCommand('setweap ' + headcmd^.str[2], true);
   387         't': ParseCommand('taunt ' + headcmd^.str[2], true);
   417         't': ParseCommand('taunt ' + headcmd^.str[2], true);
   388         'h': ParseCommand('hogsay ' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true);
   418         'h': ParseCommand('hogsay ' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true);
   389         '1'..'5': ParseCommand('timer ' + headcmd^.cmd, true);
   419         '1'..'5': ParseCommand('timer ' + headcmd^.cmd, true);
   390         else
   420         else
   391             if (headcmd^.cmd >= #128) and (headcmd^.cmd <= char(128 + cMaxSlotIndex)) then
   421             if (byte(headcmd^.cmd) >= 128) and (byte(headcmd^.cmd) <= 128 + cMaxSlotIndex) then
   392                 ParseCommand('slot ' + char(byte(headcmd^.cmd) - 79), true)
   422                 ParseCommand('slot ' + char(byte(headcmd^.cmd) - 79), true)
   393                 else
   423                 else
   394                 OutError('Unexpected protocol command: ' + headcmd^.cmd, True)
   424                 OutError('Unexpected protocol command: ' + headcmd^.cmd, True)
   395         end;
   425         end;
   396     RemoveCmd
   426     RemoveCmd
   397     end;
   427     end;
   398 
   428 
   399 if (headcmd <> nil) and tmpflag and (not CurrentTeam^.hasGone) then
   429 if (headcmd <> nil) and tmpflag and (not CurrentTeam^.hasGone) then
   400     TryDo(GameTicks < hiTicks shl 16 + headcmd^.loTime,
   430     TryDo(GameTicks < LongWord(hiTicks shl 16) + headcmd^.loTime,
   401             'oops, queue error. in buffer: ' + headcmd^.cmd +
   431             'oops, queue error. in buffer: ' + headcmd^.cmd +
   402             ' (' + IntToStr(GameTicks) + ' > ' +
   432             ' (' + IntToStr(GameTicks) + ' > ' +
   403             IntToStr(hiTicks shl 16 + headcmd^.loTime) + ')',
   433             IntToStr(hiTicks shl 16 + headcmd^.loTime) + ')',
   404             true);
   434             true);
   405 
   435 
   406 isInLag:= (headcmd = nil) and tmpflag and (not CurrentTeam^.hasGone);
   436 isInLag:= (headcmd = nil) and tmpflag and (not CurrentTeam^.hasGone);
   407 
   437 
   408 if isInLag then fastUntilLag:= false
   438 if isInLag and fastUntilLag then 
       
   439 begin
       
   440     ParseCommand('spectate 0', true);
       
   441     fastUntilLag:= false
       
   442 end;
   409 end;
   443 end;
   410 
   444 
   411 procedure chFatalError(var s: shortstring);
   445 procedure chFatalError(var s: shortstring);
   412 begin
   446 begin
   413     SendIPC('E' + s);
   447     SendIPC('E' + s);
   414     // TODO: should we try to clean more stuff here?
   448     // TODO: should we try to clean more stuff here?
   415     SDL_Quit;
   449     SDL_Quit;
   416     halt(2)
   450 
       
   451     if IPCSock <> nil then
       
   452         halt(HaltFatalError)
       
   453     else
       
   454         halt(HaltFatalErrorNoIPC);
   417 end;
   455 end;
   418 
   456 
   419 procedure doPut(putX, putY: LongInt; fromAI: boolean);
   457 procedure doPut(putX, putY: LongInt; fromAI: boolean);
   420 begin
   458 begin
   421 if CheckNoTeamOrHH or isPaused then
   459 if CheckNoTeamOrHH or isPaused then
   422     exit;
   460     exit;
   423 bShowFinger:= false;
   461 bShowFinger:= false;
   424 if not CurrentTeam^.ExtDriven and bShowAmmoMenu then
   462 if (not CurrentTeam^.ExtDriven) and bShowAmmoMenu then
   425     begin
   463     begin
   426     bSelected:= true;
   464     bSelected:= true;
   427     exit
   465     exit
   428     end;
   466     end;
   429 
   467 
   430 with CurrentHedgehog^.Gear^,
   468 with CurrentHedgehog^.Gear^,
   431     CurrentHedgehog^ do
   469     CurrentHedgehog^ do
   432     if (State and gstHHChooseTarget) <> 0 then
   470     if (State and gstChooseTarget) <> 0 then
   433         begin
   471         begin
   434         isCursorVisible:= false;
   472         isCursorVisible:= false;
   435         if not CurrentTeam^.ExtDriven then
   473         if not CurrentTeam^.ExtDriven then
   436             begin
   474             begin
   437             if fromAI then
   475             if fromAI then
   450             begin
   488             begin
   451             TargetPoint.X:= putX;
   489             TargetPoint.X:= putX;
   452             TargetPoint.Y:= putY
   490             TargetPoint.Y:= putY
   453             end;
   491             end;
   454         AddFileLog('put: ' + inttostr(TargetPoint.X) + ', ' + inttostr(TargetPoint.Y));
   492         AddFileLog('put: ' + inttostr(TargetPoint.X) + ', ' + inttostr(TargetPoint.Y));
   455         State:= State and (not gstHHChooseTarget);
   493         State:= State and (not gstChooseTarget);
   456         if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackingPut) <> 0 then
   494         if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackingPut) <> 0 then
   457             Message:= Message or (gmAttack and InputMask);
   495             Message:= Message or (gmAttack and InputMask);
   458         end
   496         end
   459     else
   497     else
   460         if CurrentTeam^.ExtDriven then
   498         if CurrentTeam^.ExtDriven then
   469 
   507 
   470     headcmd:= nil;
   508     headcmd:= nil;
   471     lastcmd:= nil;
   509     lastcmd:= nil;
   472     isPonged:= false;
   510     isPonged:= false;
   473     SocketString:= '';
   511     SocketString:= '';
   474     
   512 
   475     hiTicks:= 0;
   513     hiTicks:= 0;
   476     flushDelayTicks:= 0;
   514     flushDelayTicks:= 0;
   477     sendBuffer.count:= 0;
   515     sendBuffer.count:= 0;
   478 end;
   516 end;
   479 
   517 
   481 begin
   519 begin
   482     while headcmd <> nil do RemoveCmd;
   520     while headcmd <> nil do RemoveCmd;
   483     SDLNet_FreeSocketSet(fds);
   521     SDLNet_FreeSocketSet(fds);
   484     SDLNet_TCP_Close(IPCSock);
   522     SDLNet_TCP_Close(IPCSock);
   485     SDLNet_Quit();
   523     SDLNet_Quit();
       
   524 
   486 end;
   525 end;
   487 
   526 
   488 end.
   527 end.