-
Notifications
You must be signed in to change notification settings - Fork 170
Network Protocol
This is the draft for the new Network protocol.
The network protocol is all you really need to know if you want to create your own PO servers/clients.
PO uses binary through '''TCP''' to network thing, serializing data following the Qt specification: http://doc.qt.nokia.com/latest/datastreamformat.html. The only difference is strings being transferred in UTF-8 rather than UTF-16.
==Packet Format== ===Format Description===
- 2 bytes for the packet length ''l''. First byte is ''l''/256, second byte is ''l'' modulo 256
- 1 byte to tell which command it is (like SendMessage, BattleStarted, ...)
- ''l''-1 bytes of data, serialized with Qt's approach
You probably noticed that the maximum length you can specify is 0xFFFF. However you may need to send longer packets at times, so when a packet of length 0xFFFF is sent, another packet is expected and its contents will be appended to the previous packet. As it's just data being appended, the packet will not contain a command field. Several data packets can be appended, but for now it is recommended to only allow 16 packets tops.
Also, when data is missing in the protocol, Pokémon Online assumes the default value for them. That means that when name, info, winmessage and losemessage are expected, you can send only your name and the 3 other strings will be defaulted blank (""). This is useful to know if you only want to write a minimal client.
===Flags===
Flags are a series of bytes indicating boolean values.
The first boolean value will be encoded on the Least Significant Bit (byte & 0x01), and so on, until the 7th bit (01000000). The 8th and Most Significant Bit is used to tell if another byte of flags is coming up, it should never be used to store a boolean value. This is in case protocol evolves and more flags are needed in the future. If you expect more flags when receiving but get a 0 for the MSB, assume all remaining flags to be 0.
Flags can be used in two ways: to specify how the rest of the message will go or just as a data field. When used to specify how the rest of the message will go, they are used to say if an element is present or not in the protocol. Then when decoding the message, the receiver will use flags to read element by element if they are there. The order of the elements depending on flags and the order of the boolean values in the flags should always be the same.
'''Example''': Suppose, on client login, we can specify a channel to log in instead of the main channel.
The message the client would send would look like: <format flags: 0x00 if no channel is speficied, 0x01 if a channel is specified> If channel flag, channel name to join, else nothing?>
===Version Control=== The current version number of the protocol as of now is '''0''', and the subversion is '''0''' too.
The protocol version number is supposed to increase if and if only either:
- An backward-incompatibility with the previous version of the protocol is created, that can't be solved with simply adding Network Flags and appending data to packets.
- The version number of a version controlled datatype changes.
The subversion number should increase everytime there's a change or a combination of changes in the protocol. Both the version number and subversion number range from 0 to 65535.
Version controlled data types also exist. Those dataypes are transferred with this
format: <length: 2 bytes><Version number: 1 byte><Data: length - 1 bytes>
The version number of a version controlled datatype is supposed to change only and if only:
- An backward-incompatibility with the previous version of the protocol is created, that can't be solved with simply adding Network Flags and appending data to packets.
Example on how this should be done from the {{anc|Team}} format:
teamGen:uint8pokes:PokePersonal*6 . If we need to add a argument (several modes for a gen), it would make more sense to put it between and pokes:Personal*6. Even though we would associate a network flag to it (would we?), as we don't put the data at the end of the structure we would break compatibility. Hence, the version number of the Team format would increase and the version number of the protocol too. If we decided to put the argument after the list of pokemons, we wouldn't change the version number. It's more of a "compatibility number" than "version number"
The reason why version number is specified to increase that way is to give servers the possibility to send data differently according to the client if you want to support older client, while limiting the version number change at the maximum.
If you're a client, version control on datatypes can help you ignore higher version datatypes, while still being able to read same-version data-types which evolved through the use of network flags/appending. This is another reason to limit the version number changes to what's possible: old client will ignore the contents of version controlled datatypes if the version is too high for them, so if there's a way they can still make sense of the data we should avoid increasing the version number.
===Example Packet=== Login command is 1.
Basically when you log in you send a lot of info (team etc.) but what's only needed is the protocol version and your name, which is in a string. Strings are serialized with 4 bytes indicating the number of bytes to follow, and then the string in UTF-8. There is a flag argument needed before the name, but if we set it to 0 it says we log in with the minimal information.
So if you wanted to login with, say, "powersurge2", it would be:
2 bytes for the length 1 byte for the login command 2 bytes for the network protocol version. Would be 0 here as the current version of the protocol is zero. 1 byte for the flags argument. Setting it to 0 means no version number or type of client before the name information. 4 bytes for the number of bytes of the string 11 bytes for "powersurge2" in UTF-16 (as those characters are ascii, they translate as only one byte per character)
Seeing as this is a 21 bytes message, the 2 bytes for the length of the message would be 0 and 21.
The final byte per byte would be:
[0 18] [1] [0 0] [0] [0 0 0 11] ['p' 'o' 'w' 'e' 'r' 's' 'u' 'r' 'g' 'e' '2']
==Packet Types==
*ZipCommand
**'''Format:''' <contentsType: uint8>
***If contentsType is 0, means that the data is a single deflated packet: 1 byte for the command, the rest for the data, however long that data is.
***If contentsType is 1, means that the data represents a group of network packets. Network packets are 2 bytes for length, 1 byte for command and the rest of data, and if the length of a network packet exceeds what 2 bytes can describe then the standard protocol for extended length network packets is applied.
**'''Note:''' ZipCommand should only be used when the other party supports it.
*LoginV2
**'''Client->Server:''' <Protocol version: {{anc|ProtocolVersion}}> Type of client: string ?> Version number of client: uint16 ?> reconnectBits: byte ?> Channel to join: string?> Additional channels: QStringList ?> Trainer Color: color ?> Trainer info: TrainerInfo ?> ?> Event specification: flags ?>
***'''Network Flags:''' hasClientType, hasVersionNumber, hasReconnect, hasDefaultChannel, hasAdditionalChannels, hasColor, hasTrainerInfo, hasNewTeam, hasEventSpecification.
***'''Data Flags:''' supportsZipCompression, showsTeam, isLadderEnabled, isIdle, wantsIdsWithMessages
***'''Event Flags:''' idle events, battle events, player update events, team change events. Player update events would not include the first time a player is seen, and name changes are always team change events.
***'''Reconnect:''' If the network flag is off, client will never be reconnected. Otherwise, client can be reconnected with any IP with the last reconnectBits bits that are different. For example, if reconnectBits is 0, the player will only be able to reconnect from the same IP, if reconnectBits is 8 then the player will be able to reconnect form an IP with the last 8 bits different (the last component of an ip v4 address), if it's 255 he will be able to reconnect with any IP. The lower the value is, the more secure it is.
***'''Team Stuff:''' The client has the possibility to send several teams. A team's id is the order in which it was sent, for example the id of the third team sent is 2. For now the "official" limit is 5 teams at a time.
***'''Note:''' If the default channel flag is specified and an empty name is provided for the channel, the server will interpret this as the client not wanting to join any channels
***'''Note 2:''' wantsIdsWithMessage is used in all the chat message events, to decide whether or not the client wants a message in the form "Chocobo: Hi my name is chocobo" or <"Hi my name is chocobo">
*Login
**'''Server->Client:''' Reconnect Pass: bytearray?>info:{{anc|PlayerInfo}} - Validates login and gives info about yourself (player id, ...)
***'''Network Flags:''' hasReconnectPass
***'''Note:''' When the client requests to be able to reconnect, and the server supports it, the server sends the client a reconnect pass supposedly randomly generated. When the client tries to reconnect, it must provide the same pass in order to prove itself.
*Logout
**'''Server->Client:''' <id: uint32> - Says that player X logged out. What's mostly sent is channel leave messages, but when two people started a PM conversation it's wise to warn them the other logged out
**'''Client->Server:''' Used to tell that the client leaves cleanly and so there is no need to keep any reconnect information serverside.
*SendMessage
**'''Server->Client:''' channel: uint32 ?> id: uint32 ?>message:string - Sends a chat message to the client.
***'''Network Flags:''' hasChannel, hasId
****When no channel is specified, the message is just meant for the client to receive, however the client decides to implement it (server PM window, message on all channels, ...)
****hasId can only be true if the client requested messages to use ids if possible. In that case it's the id of the sender (with 0 being Server), and message is the body of the message. If id is not specified, then message is the whole message to display, including bot/player name. Basic clients will want to not ask for ids on login, so they can just display every message without keeping track of names associated with player IDs.
***'''Data flags:''' isHtml
**'''Client->Server:''' channel: uint32 ?>message:string - sends a message
***'''Network Flags:''' hasChannel - note that hasChannel == false would mean broadcoast a message throughout the server. It would require special permissions for the client to do so though.
***'''Data flags:''' isHtml - Requires special permissions from the server too.
PlayersList
**'''Registry->Client:''' name:string desc:string numPlayers:uint16 ip:string maxPlayers:uint16 port:uint16 - Sends a server and its info to the client. Note: The registry automatically sends the server data when a client connects to its port 5081 (host is pokemon-online.dynalias.net).
**'''Server->Client:''' <infos: {{anc|PlayerInfo}}> - Sends several player infos to the client. When several are sent at the same time, it usually indicates that you are about to join a channel with said players, otherwise the info of a player just updated. This is a good example of when zip compression can be useful.
*SendTeam
**'''Client->Server:''' trainerInfo:{{anc|TrainerInfo}} ?> <##><#*#> ?> - Tells the server that we are changing teams
***'''Network Flags''': hasTrainerInfo, hasTeamChange
***'''Team Stuff:''' Either we choose to replace all teams, or only some. This is described by keepsOldTeams.
****In case we replace all teams, the number of teams is sent followed by those teams
****In case we only change some teams, for each team changed the id of the team followed by the team itself are sent.
**'''Server->Client:''' name:string ?> info:string ?> ?> < ( pokes:{{anc|pokeid}}*6 ?> rating:uint16 ?>)*numberofteams > ?> - Tell a player that some info about the player changed
***'''Network Flags:''' hasNameChanged, hasInfoChanged, hasRatingChanged, hasChangedTeams, showsTeam, showsRating
*ChallengeStuff
**'''Client->Server:''' <challenge info:{{anc|ChallengeInfo}}> - The info regarding the challenge (opponent, status, team, ...)
**'''Server->Client:''' <challenge info:{{anc|ChallengeInfo}}> - The info regarding the challenge (opponent, status, team, ...)
*EngageBattle
**'''Server->Client:''' <battleid: uint32><{{PAGENAME}}#Battle Mode: uint8>player1:uint32player2:uint32 ?>
***'''Network Flags:''' isOwnBattle
***'''Note:''' The data after the mode could change format if mode isn't Singles/Doubles/Triples/Rotation. If you don't reckognise the battleMode, it's advised to ignore the data after it.
*BattleFinished
**'''Client->Server:''' <battleId: uint32> - Forfeits/Closes the battle
**'''Server->Client:''' <battleId: uint32><{{PAGENAME}}#Battle Result: uint8><{{PAGENAME}}#Battle Mode:uint8>winner:uint32loser:uint32
***'''Note:''' The data after the mode could change format if mode isn't Singles/Doubles/Triples/Rotation. If you don't reckognise the battleMode, it's advised to ignore the data after it.
*BattleMessage = 10
*BattleChat
*KeepAlive
**'''Server->Client:''' <number: uint16> - This command is used to keep the connection alive with clients. The number is a ping number that the client must send back
**'''Client->Server:''' <number: uint16> - This command is used to keep the connection alive with clients. The number is the number of the ping we answer to (the one sent by the server)
*AskForPass
*Register
*PlayerKick
*PlayerBan
*ServerInfoChanged
** newname:string ?> new max number:quint16 ?> - Tells the client something about the characteristics of the server changed
*** '''Network Flags:''' hasNameChanged, hasMaxNumChanged, hasDescriptionChanged
*Reconnect
**'''Client->Server:''' <id: uint32> <secret: bytearray> <numberofcommands: uint32> - Attempts to reconnect an account that was disconnected.
***'''id:''' The id the user had before being disconnected.
***'''secret:''' The hash the server sent to the client in case it needed reconnect, in the ''Login'' packet.
***'''numberofcommands:''' The total number of commands the client received since it connected to the server, including the Keep Alive commands. Used to tell the server where to catch up.
**'''Server->Client:''' <isSuccessful: bool><{{PAGENAME}}#Reconnect Failure: uint8> - Used to tell if a reconnect attempt is successful or not
***'''Note: ''' For successful attempts it's advised not to send this message at all but rather send all the 'catchup' data. This may change in the future
*Unused command
*SendPM = 20
**'''Client->Server:''' <playerId: uint32><message: string> - Sends a PM to the given player
**'''Server->Client:''' <playerId: uint32><message: string> - Receives a PM from the given player
*Away
**'''Client->Server:''' away:bool - Informs server that a player is away or back.
**'''Server->Client:''' <playerId: uint32><away: bool> - Informs client that a player is now away or back.
*GetUserInfo
*GetUserAlias
*GetBanList
*CPBan
*CPUnban
*SpectateBattle
*SpectatingBattleMessage
*SpectatingBattleChat
*SpectatingBattleFinished = 30
*LadderChange
*ShowTeamChange
*VersionControl
**Server->Client
***'''Format:''' <Protocol version: {{anc|ProtocolVersion}}><Latest network protocol with new features:{{anc|ProtocolVersion}}><Latest network protocol with with compatibility breaks: {{anc|ProtocolVersion}}><latest network protocol with major compatibility breaks: {{anc|ProtocolVersion}}> - Sent to the client as soon as connection is established, gives info about version of the server
****'''Data Flags:''' supportsZipCompression - does the server support zip compressing?
*TierSelection
*ServMaxChange
*FindBattle
*ShowRankings
*Announcement
*CPTBan
*CPTUnban = 40
*PlayerTBan
*GetTBanList
*BattleList
*ChannelsList
*ChannelPlayers
*JoinChannel
**'''Client->Server:''' channelName:string - Informs server that a player wants to join a channel name channelName. Note: the channel will be created if it doesn't exist (no guarantee on that - server may prevent it).
**'''Server->Client:''' <channelId: uint32><playerId: uint32> - Informs client that a player has joined a channel.
*LeaveChannel
**'''Client->Server:''' <channelId: uint32> - Informs server that a player wants to leave a channel.
**'''Server->Client:''' <channelId: uint32><playerId: uint32> - Informs client that a player has left a channel.
*ChannelBattle
*RemoveChannel
**'''Server->Client:''' <channelId: uint32> - Informs client that a channel has been removed.
*AddChannel = 50
**'''Server->Client:''' <channelId: uint32>channelName:string - Informs client that a new channel has been created.
*Unused
*ChanNameChange
**'''Server->Client:''' <channelId: uint32>channelName:string - Informs client that a channel changes name. Note: This is basically for the main channel name changes, as it doesn't make sense to change the name of other channels.
*Unused
*Unused
*Unused
*SpecialPass
*ServerListEnd
**'''Registry->Client:''' - Notifies the client that all the server list was sent
==Data Structures==
===TrainerInfo=== TrainerInfo is {{PAGENAME}}#Version Control.
'''Format:''' <avatar: uint16><info: string>?>
'''Network Flags:''' hasBattleMessages
===Team=== Team is {{PAGENAME}}#Version Control.
'''Format:''' defaultTier: string ?>teamGen:{{anc|generation}} numberOfPokemon:uint8?>pokes:{{anc|PokePersonal}}*hasNumberOfPokemon?numberOfPokemon:6
'''Network Flags:''' hasDefaultTier, hasNumberOfPokemon
===PokePersonal=== PokePersonal is {{PAGENAME}}#Version Control.
'''Format:''' gen: {{anc|generation}} ?>num:{{anc|pokeid}}level:uint8 nick:string ?><# item:uint16 #><# ability:uint16 #><# nature:uint8 #><# gender:uint8 #><#? happiness:uint8 ?#>< pp ups: uint8 ?><move: uint16>4 ><EV: uint86> IV: uint8*6 ?>
'''Network flags:''' hasGen, hasNickname, hasHappiness, hasPPups, hasIVs
'''Data flags:''' isShiny
Note that tags marked with # are definitely not present when they are too recent for the gen of the pokemon. For example item will never be included for gen 1, ability will never be included for gen 1 & 2, ...
The gen data shouldn't be necessary and the official client won't use them: They are already specified in the team. However, we never know about the future, maybe one day we will need to send a pokemon individually to the server.
===pokeid===
num:uint16formeNum:uint8
For example, Shaymin-S has num = 492 and formeNum = 1, and pokemons without formes have formeNum = 0.
===PlayerInfo=== PlayerInfo is {{PAGENAME}}#Version Control.
'''Format:'''id:uint32avatar:uint16name:stringinfo:stringauth:int8numberOfTiers:uint8<(tier:stringgen:{{anc|generation}} pokemons:{{anc|pokeid}}*6 ?> rating:int16 ?>)*numberOfTiers>color:QColorstate:uint8
'''Network Flags:'''showsTeam, showsRating
The tiers correspond to the teams of the player: the first tier to the first team, and so on. Thus, someone can have the same tier several times.
Flags are added up from the following array: {| border="1" ! State
! number |
---|
LoggedIn |
1 |
- |
Battling |
2 |
- |
Away |
4 |
} |
===ChallengeInfo===
description:uint8playerId:int32team:uint8clauses:uint32<{{PAGENAME}}#Battle_Mode:uint8>
Description tells what kind of information is sent:
{| border="1"
! Mode
! number |
---|
Sent |
0 |
- |
Accepted |
1 |
- |
Cancelled |
2 |
- |
Busy |
3 |
- |
Refused |
4 |
- |
Invalid team |
5 |
- |
Invalid gen |
6 |
} |
Clauses are added up from the following array: |
{ |
!Clause |
!number |
- |
Sleep Clause |
1 |
- |
Freeze Clause |
2 |
- |
Disallow specs |
4 |
- |
Item Clause |
8 |
- |
Challenge Cup |
16 |
- |
No timeout |
32 |
- |
Species Clause |
64 |
- |
Wifi Clause |
128 |
- |
Self KO Clause |
256 |
} |
===Battle Mode=== {| border="1" ! Mode
! number |
---|
Singles |
0 |
- |
Doubles |
1 |
- |
Triples |
2 |
- |
Rotation |
3 |
} |
===Battle Result=== {| border="1" ! Result
! number |
---|
Forfeit |
0 |
- |
Win |
1 |
- |
Tie |
2 |
- |
Close |
3 |
} |
===BattleConfiguration===
'''Format:''' <gen: {{anc|generation}}><{{PAGENAME}}#Battle_Mode:uint8>clauses:uint32ids:uint32*hasNumberOfIds?2:numberOfIds
'''Network Flags:''' hasNumberOfIds
'''Data Flags:''' isRated
===TeamBattle=== pokemons:{{anc|PokeBattle*6}}
===PokeBattle===
'''Format:''' num:{{anc|pokeidnick:stringtotalHP:uint16HP:uint16<gender: uint8><level: uint8><item: uint16><ability: uint16><happiness: uint8>stats:uint16*5<moves: {{anc|BattleMove}}*4>evs:uint8*6ivs:uint8*6
'''Data Flags:''' isShiny
===BattleMove=== <movenum: uint16><PPs: uint8><totalPPs: uint8>
===generation=== <gen: uint8><subgen: uint8>
1-0: Stadium
1-1: RBY
1-2: Stadium with tradebacks
2-0: Stadium 2
2-1: GSC
3-0: ADV
3-1: RSE 200
4-0: HGSS
4-1: DP
4-2: DPPt
5-0: BW
===Reconnect Failure=== In any case, a reconnect failure with wrong identification info or no reconnect data indicates it may be better to stop trying. The only case is the IP subrange, but then you have to change your IP to a closer one. {| border="1" ! Reason
! number |
---|
No reconnect data - time out |
0 |
- |
Wrong secret number |
1 |
- |
Not enough command history to catch up |
2 |
- |
Wrong IP subrange |
3 |
} |
===ProtocolVersion=== '''Format:'''
See {{PAGENAME}}#Version Control for more info.