# HG changeset patch # User unc0rr # Date 1431548500 -10800 # Node ID 8ebf01f75d9f17d4ecd8a701f85fef6a6d8eb232 # Parent 336f5ad638bed4acef76e48102a2882c383c4691 Mockup of protocol parser diff -r 336f5ad638be -r 8ebf01f75d9f hedgewars/uFLNet.pas --- a/hedgewars/uFLNet.pas Mon May 11 00:27:16 2015 +0300 +++ b/hedgewars/uFLNet.pas Wed May 13 23:21:40 2015 +0300 @@ -8,7 +8,173 @@ implementation uses SDLh; +type TCmdType = (cmd_ASKPASSWORD, cmd_BANLIST, cmd_BYE, cmd_CHAT, cmd_CLIENT_FLAGS, cmd_CONNECTED, cmd_EM, cmd_HH_NUM, cmd_INFO, cmd_JOINED, cmd_JOINING, cmd_KICKED, cmd_LEFT, cmd_LOBBY_JOINED, cmd_LOBBY_LEFT, cmd_NICK, cmd_NOTICE, cmd_PING, cmd_PROTO, cmd_ROOMS, cmd_ROUND_FINISHED, cmd_RUN_GAME, cmd_SERVER_AUTH, cmd_SERVER_MESSAGE, cmd_SERVER_VARS, cmd_TEAM_ACCEPTED, cmd_TEAM_COLOR, cmd_WARNING, cmd___UNKNOWN__); +type + TNetState = (netDisconnected, netLoggedIn); + TParserState = record + cmd: TCmdType; + l: LongInt; + netState: TNetState; + end; + PHandler = procedure; + +var state: TParserState; + +// generated stuff here +const letters: array[0..235] of char = ('A', 'S', 'K', 'P', 'A', 'S', 'S', 'W', 'O', 'R', 'D', #10, #0, 'B', 'A', 'N', 'L', 'I', 'S', 'T', #10, #0, 'Y', 'E', #10, #0, 'C', 'H', 'A', 'T', #10, #0, 'L', 'I', 'E', 'N', 'T', '_', 'F', 'L', 'A', 'G', 'S', #10, #0, 'O', 'N', 'N', 'E', 'C', 'T', 'E', 'D', #10, #0, 'E', 'M', #10, #0, 'H', 'H', '_', 'N', 'U', 'M', #10, #0, 'I', 'N', 'F', 'O', #10, #0, 'J', 'O', 'I', 'N', 'E', 'D', #10, #0, 'I', 'N', 'G', #10, #0, 'K', 'I', 'C', 'K', 'E', 'D', #10, #0, 'L', 'E', 'F', 'T', #10, #0, 'O', 'B', 'B', 'Y', ':', 'J', 'O', 'I', 'N', 'E', 'D', #10, #0, 'L', 'E', 'F', 'T', #10, #0, 'N', 'I', 'C', 'K', #10, #0, 'O', 'T', 'I', 'C', 'E', #10, #0, 'P', 'I', 'N', 'G', #10, #0, 'R', 'O', 'T', 'O', #10, #0, 'R', 'O', 'O', 'M', 'S', #10, #0, 'U', 'N', 'D', '_', 'F', 'I', 'N', 'I', 'S', 'H', 'E', 'D', #10, #0, 'U', 'N', '_', 'G', 'A', 'M', 'E', #10, #0, 'S', 'E', 'R', 'V', 'E', 'R', '_', 'A', 'U', 'T', 'H', #10, #0, 'M', 'E', 'S', 'S', 'A', 'G', 'E', #10, #0, 'V', 'A', 'R', 'S', #10, #0, 'T', 'E', 'A', 'M', '_', 'A', 'C', 'C', 'E', 'P', 'T', 'E', 'D', #10, #0, 'C', 'O', 'L', 'O', 'R', #10, #0, 'W', 'A', 'R', 'N', 'I', 'N', 'G', #10, #0, '$', #10, #0); + +const commands: array[0..235] of integer = (13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, 13, 8, 0, 0, 0, 0, 0, 0, -37, 0, 0, 0, -36, 29, 5, 0, 0, 0, -35, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -34, 0, 0, 0, 0, 0, 0, 0, 0, 0, -33, 4, 0, 0, -32, 8, 0, 0, 0, 0, 0, 0, -31, 6, 0, 0, 0, 0, -30, 13, 0, 0, 0, 4, 0, 0, -29, 0, 0, 0, 0, -28, 8, 0, 0, 0, 0, 0, 0, -27, 25, 5, 0, 0, 0, -26, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, -25, 0, 0, 0, 0, 0, -24, 13, 5, 0, 0, 0, -23, 0, 0, 0, 0, 0, 0, -22, 12, 5, 0, 0, 0, -21, 0, 0, 0, 0, 0, -20, 30, 20, 5, 0, 0, 0, -19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -18, 0, 0, 0, 0, 0, 0, 0, 0, -17, 28, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, -16, 9, 0, 0, 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, -14, 22, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, 0, -12, 9, 0, 0, 0, 0, 0, 0, 0, -11, 0, 0, -10); + +procedure handler_ASKPASSWORD; +begin + state.cmd:= cmd_ASKPASSWORD; +end; + +procedure handler_BANLIST; +begin + state.cmd:= cmd_BANLIST; +end; + +procedure handler_BYE; +begin + state.cmd:= cmd_BYE; +end; + +procedure handler_CHAT; +begin + state.cmd:= cmd_CHAT; +end; + +procedure handler_CLIENT_FLAGS; +begin + state.cmd:= cmd_CLIENT_FLAGS; +end; + +procedure handler_CONNECTED; +begin + state.cmd:= cmd_CONNECTED; +end; + +procedure handler_EM; +begin + state.cmd:= cmd_EM; +end; + +procedure handler_HH_NUM; +begin + state.cmd:= cmd_HH_NUM; +end; + +procedure handler_INFO; +begin + state.cmd:= cmd_INFO; +end; + +procedure handler_JOINED; +begin + state.cmd:= cmd_JOINED; +end; + +procedure handler_JOINING; +begin + state.cmd:= cmd_JOINING; +end; + +procedure handler_KICKED; +begin + state.cmd:= cmd_KICKED; +end; + +procedure handler_LEFT; +begin + state.cmd:= cmd_LEFT; +end; + +procedure handler_LOBBY_JOINED; +begin + state.cmd:= cmd_LOBBY_JOINED; +end; + +procedure handler_LOBBY_LEFT; +begin + state.cmd:= cmd_LOBBY_LEFT; +end; + +procedure handler_NICK; +begin + state.cmd:= cmd_NICK; +end; + +procedure handler_NOTICE; +begin + state.cmd:= cmd_NOTICE; +end; + +procedure handler_PING; +begin + state.cmd:= cmd_PING; +end; + +procedure handler_PROTO; +begin + state.cmd:= cmd_PROTO; +end; + +procedure handler_ROOMS; +begin + state.cmd:= cmd_ROOMS; +end; + +procedure handler_ROUND_FINISHED; +begin + state.cmd:= cmd_ROUND_FINISHED; +end; + +procedure handler_RUN_GAME; +begin + state.cmd:= cmd_RUN_GAME; +end; + +procedure handler_SERVER_AUTH; +begin + state.cmd:= cmd_SERVER_AUTH; +end; + +procedure handler_SERVER_MESSAGE; +begin + state.cmd:= cmd_SERVER_MESSAGE; +end; + +procedure handler_SERVER_VARS; +begin + state.cmd:= cmd_SERVER_VARS; +end; + +procedure handler_TEAM_ACCEPTED; +begin + state.cmd:= cmd_TEAM_ACCEPTED; +end; + +procedure handler_TEAM_COLOR; +begin + state.cmd:= cmd_TEAM_COLOR; +end; + +procedure handler_WARNING; +begin + state.cmd:= cmd_WARNING; +end; + +procedure handler___UNKNOWN__; +begin + state.cmd:= cmd___UNKNOWN__; +end; + +const handlers: array[0..28] of PHandler = (@handler___UNKNOWN__, @handler_WARNING, @handler_TEAM_COLOR, @handler_TEAM_ACCEPTED, @handler_SERVER_VARS, @handler_SERVER_MESSAGE, @handler_SERVER_AUTH, @handler_RUN_GAME, @handler_ROUND_FINISHED, @handler_ROOMS, @handler_PROTO, @handler_PING, @handler_NOTICE, @handler_NICK, @handler_LOBBY_LEFT, @handler_LOBBY_JOINED, @handler_LEFT, @handler_KICKED, @handler_JOINING, @handler_JOINED, @handler_INFO, @handler_HH_NUM, @handler_EM, @handler_CONNECTED, @handler_CLIENT_FLAGS, @handler_CHAT, @handler_BYE, @handler_BANLIST, @handler_ASKPASSWORD); + + +// end of generated stuff var sock: PTCPSocket; fds: PSDLNet_SocketSet; netReaderThread: PSDL_Thread; @@ -28,18 +194,37 @@ if r > 0 then begin sockbufpos:= 1; - sockbuf[0]:= char(i); + sockbuf[0]:= char(r); getNextChar:= sockbuf[1]; end else begin sockbufpos:= 0; sockbuf[0]:= #0; getNextChar:= #0 - end; + end + end end; function netReader(data: pointer): LongInt; cdecl; export; +var c: char; begin +repeat + c:= getNextChar; + if c = #0 then + state.netState:= netDisconnected; + if c = letters[state.l] then + if commands[state.l] < 0 then + handlers[-10 - commands[state.l]]() + else + inc(state.l) + else + if commands[state.l] = 0 then + // unknown cmd + else + repeat + inc(state.l, commands[state.l]) + until (letters[state.l] = c) or (commands[state.l] = 0) +until state.netState = netDisconnected end; procedure connectOfficialServer; diff -r 336f5ad638be -r 8ebf01f75d9f tools/protocolParser.hs --- a/tools/protocolParser.hs Mon May 11 00:27:16 2015 +0300 +++ b/tools/protocolParser.hs Wed May 13 23:21:40 2015 +0300 @@ -33,6 +33,10 @@ , cmd1 "ASKPASSWORD" SS , cmd1 "SERVER_AUTH" SS , cmd1 "JOINING" SS + , cmd1 "TEAM_ACCEPTED" SS + , cmd1 "HH_NUM" $ Many [SS] + , cmd1 "TEAM_COLOR" $ Many [SS] + , cmd1 "TEAM_ACCEPTED" SS , cmd1 "BANLIST" $ Many [SS] , cmd1 "JOINED" $ Many [SS] , cmd1 "LOBBY:JOINED" $ Many [SS] @@ -40,21 +44,30 @@ , cmd2 "CLIENT_FLAGS" SS $ Many [SS] , cmd2 "LEFT" SS $ Many [SS] , cmd1 "SERVER_MESSAGE" LS + , cmd1 "ERROR" LS + , cmd1 "NOTICE" LS + , cmd1 "WARNING" LS + , cmd1 "JOINING" SS , cmd1 "EM" $ Many [LS] , cmd1 "PING" $ Many [SS] , cmd2 "CHAT" SS LS , cmd2 "SERVER_VARS" SS LS , cmd2 "BYE" SS LS , cmd1 "INFO" $ Many [SS] + , cmd1 "ROOMS" $ Many [SS] , cmd "KICKED" [] + , cmd "RUN_GAME" [] + , cmd "ROUND_FINISHED" [] ] +unknowncmd = PTPrefix "$" [PTCommand "$" $ Command "__UNKNOWN__" [Many [SS]]] + groupByFirstChar :: [ParseTree] -> [(Char, [ParseTree])] groupByFirstChar = MM.assocs . MM.fromList . map breakCmd makePT cmd@(Command n p) = PTCommand n cmd -buildParseTree cmds = [PTPrefix "!" $ bpt $ map makePT cmds] +buildParseTree cmds = [PTPrefix "!" $ (bpt $ map makePT cmds) ++ [unknowncmd]] bpt cmds = if isJust emptyNamed then cmdLeaf $ fromJust emptyNamed else subtree where emptyNamed = find (\(_, (PTCommand n _:_)) -> null n) assocs @@ -80,25 +93,33 @@ zeroChar = text "#0: state:= pstDisconnected;" elsePart = text "else end;" -renderArrays (letters, commands, handlers) = l $+$ s $+$ c +renderArrays (letters, commands, handlers) = vcat $ punctuate (char '\n') [cmds, l, s, bodies, c] where maybeQuotes s = if null $ tail s then quotes $ text s else text s l = text "const letters: array[0.." <> (int $ length letters - 1) <> text "] of char = " <> parens (hsep . punctuate comma $ map maybeQuotes letters) <> semi s = text "const commands: array[0.." <> (int $ length commands - 1) <> text "] of integer = " <> parens (hsep . punctuate comma $ map text commands) <> semi - c = text "const handlers: array[0.." <> (int $ length handlers - 1) <> text "] of integer = " - <> parens (hsep . punctuate comma $ map (text . mangle . fixName) handlers) <> semi - mangle = (++) "handler_" + c = text "const handlers: array[0.." <> (int $ length fixedNames - 1) <> text "] of PHandler = " + <> parens (hsep . punctuate comma $ map (text . (++) "@handler_") $ reverse fixedNames) <> semi + fixedNames = map fixName handlers fixName = map fixChar fixChar c | isLetter c = c | otherwise = '_' + bodies = vcat $ punctuate (char '\n') $ map handlerBody fixedNames + handlerBody n = text "procedure handler_" <> text n <> semi + $+$ text "begin" + $+$ nest 4 ( + text "state.cmd:= cmd_" <> text n <> semi + ) + $+$ text "end" <> semi + cmds = text "type TCmdType = " <> parens (hsep $ punctuate comma $ map ((<>) (text "cmd_") . text) fixedNames) <> semi pas = renderArrays $ buildTables $ buildParseTree commands where buildTables cmds = let (_, _, _, t1, t2, t3) = foldr walk (0, [0], -10, [], [], [[]]) cmds in (tail t1, tail t2, concat t3) walk (PTCommand _ (Command n params)) (lc, s:sh, pc, tbl1, tbl2, (t3:tbl3)) = - (lc, 2:sh, pc - 1, "#10":"0":tbl1, "0":show pc:tbl2, (n:t3):tbl3) + (lc, 2:sh, pc - 1, "#10":"#0":tbl1, "0":show pc:tbl2, (n:t3):tbl3) walk (PTPrefix prefix cmds) l = lvldown $ foldr fpf (foldr walk (lvlup l) cmds) prefix lvlup (lc, sh, pc, tbl1, tbl2, tbl3) = (lc, 0:sh, pc, tbl1, tbl2, []:tbl3) lvldown (lc, s1:s2:sh, pc, tbl1, t:tbl2, t31:t32:tbl3) = (lc, s1+s2:sh, pc, tbl1, (if null t32 then "0" else show s1):tbl2, (t31 ++ t32):tbl3)