-
Notifications
You must be signed in to change notification settings - Fork 171
Network Protocol
This is the draft for the new Network protocol. For the network protocol of v1 see Network Protocol v1. 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.digia.com/qt/datastreamformat.html). The only difference is strings being transferred in UTF-8 rather than UTF-16.
##Packet Format ###Format Description
- 4 bytes for the packet length
l
. First byte isl/256*256*256
, last byte isl modulo 256
- 1 byte to tell which command it is (like
SendMessage
,BattleStarted
, ...) -
l-1
bytes of data, serialized with Qt's approach
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:
<Login Command><Protocol Version Info><format flags: 0x00 if no channel is specified, 0x01 if a channel is specified><player name><? If channel flag, channel name to join, else nothing?><other info....>>
###Version Control The current version number of the protocol as of now is 3, and the subversion is 2.
The protocol version number is supposed to increase if and if only either:
- A 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 every time 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: quint16><Version number: quint8><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 Team format:
<NetworkFlags><teamGen:uint8><pokes:PokePersonal*6>.
If we need to add a <mode>
argument (several modes for a gen), it would make more sense to put it between <teamGen>
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 <mode>
argument after the list of Pokémon, 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:
- 4 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-8 (as those characters are ascii, they translate as only one byte per character)
Seeing as this is a 19 bytes message (you don't count the length), the 4 bytes for the length of the message would be 0, 0, 0 and 19.
The final byte per byte would be: [0 0 0 19] [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><length of total uncompressed data: quint32><deflated data: char*>
- If contentsType is 0, means that the data is a single deflated packet: 1 byte for the command, the rest for the data.
- If contentsType is 1, means that the data represents a group of network packets. Network packets are 4 bytes for length, 1 byte for command and the rest of data.
- Note: ZipCommand should only be used when the other party supports it.
#####Login
-
Client->Server: <Protocol version: {{anc|ProtocolVersion}}><Network Flags><? Type of client: string ?><? Version number of client: uint16 ?><trainer name: string><Data flags><? reconnectBits: byte ?><? Channel to join: string?><? Additional channels: QStringList ?><? Trainer Color: color ?><? Trainer info: {{anc|TrainerInfo}} ?><? <numberOfTeams: uint8><team: Team*numberOfTeams> ?><? Event specification: flags ?><? pluginList: list[string] ?><? cookie: string ?><? isUnique: bool ?><? id: string ?>
- Network Flags: hasClientType, hasVersionNumber, hasReconnect, hasDefaultChannel, hasAdditionalChannels, hasColor, hasTrainerInfo, hasNewTeam, hasEventSpecification, hasPluginList, hasCookie, hasUniqueId.
- Data Flags: supportsZipCompression, isLadderEnabled, wantsIdsWithMessages, isIdle
- 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 first reconnectBits bits that are in common. For example, if reconnectBits is 0, the player will be able to reconnect with any IP. If reconnectBits is 8 then the player will be able to reconnect form an IP with the first 8 bits in common (the first component of an ip v4 address), if it's 255 he will be able to reconnect with only the same IP. The higher 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 <id of chocbo><"Hi my name is chocobo">
- Note 3: The plugin list is for the future, when client plugins will be able to use the server as a relay to do stuff like chess or go battles!
-
Server->Client: <Network Flags><? Reconnect Pass: bytearray?><info:{{anc|PlayerInfo}}><team tiers: list[string]> - 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.
- Note: Team tiers correspond to the tiers for each team of the client
#####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, not including Reconnect 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
#####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: <Network Flags><Data Flags><? 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
-
Network Flags: hasChannel, hasId
-
Client->Server: <Network Flags><Data Flags><? 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.
- Client->Server: <player: int> - Requests info about a user, server will return the {{anc|PlayerInfo}} corresponding.
#####SendTeam
-
Client->Server: <Network Flags><? name: string ?><? color: QColor ?><? trainerInfo:{{anc|TrainerInfo}} ?><? <keepsOldTeams:bool><#<numberOfTeams:uint8><team:{{anc|Team}}numberOfTeams>#><#<teamChanged:uint8><team:{{anc|Team}}>#> ?> - Tells the server that we are changing teams
- Network Flags: hasName, hasColor, 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: <Network Flags><? name:string ?><? team tiers: list[string] ?> - Tell a player that some info about themselves changed
- Network Flags: hasNameChanged, hasChangedTeams
#####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: <Network Flags><battle: Battle><? <details:{{anc|BattleDetails}}> <team: {{anc|TeamBattle}}> <?items: hash(quint16,quint16) ?> ?>
- Network Flags: isOwnBattle, hasItems
- Note: The data after the mode could change format if mode isn't Singles/Doubles/Triples/Rotation. If you don't recognize the battleMode, it's advised to ignore the data after it.
#####BattleFinished
- Note2: The command can be sent to you if you reconnect, if you had battles underway. If you have the battle windows still open, then just change the team you have in them to the one specified
- Client->Server: <battleId: uint32> - Forfeits/Closes the battle
-
Server->Client: <battleId: uint32><{{PAGENAME}}#Battle Result: uint8><{{PAGENAME}}#Battle Mode:uint8><winner:uint32><loser:uint32>
- Note: The data after the mode could change format if mode isn't Singles/Doubles/Triples/Rotation. If you don't recognize the battleMode, it's advised to ignore the data after it.
#####BattleMessage = 10
- Server->Client: <battleid: uint32><data: bytearray> - Sends a byte array to extract by the battle manager
- Client->Server: <battleid: uint32><choice: {{anc|BattleChoice}}> - Sends a battle action to the server
#####BattleChat
- Client->Server: <battleid: uint32><message: string> - Sends a message in a battle window
#####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
- Server->Client: <salt: bytearray> - asks for password with the given salt. Salt should be 6 characters or more, otherwise you're advised to display a warning to the user (about insecure connection).
-
Client->Server: <hash:bytearray> - hash requested for the username. It's in binary form (ie 2 hex characters = 1 byte)
- Note: Hash to be sent to the server is MD5(<pass>+salt). PO Client traditionally use MD5(password).toHex() for <pass>, so it would be a good idea to use the same thing, if you want someone using your client to be able to use the same accounts on their favorite servers.
#####Register
- Client->Server: Makes a request to register our name
- Server->Client: Tells the client his alt is not registered
#####PlayerKick
- Server->Client: <kicked: uint32><kicker: uint32> - kicked was kicked by kicker! kicker == 0 means it's the server.
- Server->Client: <kicked: uint32> - request to kick the player designated by kicked.
#####PlayerBan
- Server->Client: <banned: uint32><banner: uint32> - banned was banned by banner! banner == 0 means it's the server.
- Server->Client: <banned: uint32> - request to ban the player designated by banned.
#####ServerInfoChanged
- <Network Flags><? newname:string ?><? new max number:quint16 ?> - Tells the client something about the characteristics of the server changed
- Network Flags: hasNameChanged, hasMaxNumChanged, hasDescriptionChanged
#####ServDescChange
#####ServNameChange
#####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
#####Options Changed
-
Client->Server: <Data Flags> - Informs server that options were changed.
- Data Flags: isLadderEnabled, isIdle
-
Server->Client: <playerid: uint32><Data Flags> - Informs client that options were changed about a user.
- Data Flags: isLadderEnabled, isIdle
#####GetUserInfo
- Client->Server: <player:string> - Requests infos about a user, available only to auth 1 and more.
- Server->Client: <info:{{anc|UserInfo}}> - Gives the info requested to the client
#####GetUserAlias
- Server->Client: <aliases: stringlist> - Gives a list of aliases belonging to the last looked up user
#####GetBanList
- Client->Server: Requests the banlist
- Server->Client: <banlist: list[string, bytearray, uint32]> - The list of banned people (name, ip, ban_expire_timestamp). It's advised to compute that list in a zip, serverside, beforehand. Ban expire time is 0 for permanent bans, otherwise it is in unix time (seconds since Jan 1st 1970) for the ban expiration.
#####CPBan
- Client->Server: <name: string, time: uint32> - Requests a ban (possibly on somebody offline). time is minutes, 0 means permanent.
#####CPUnban
- Client->Server: <name: string> - Requests an unban (possibly on somebody offline)
#####SpectateBattle
-
Client->Server: <battleid: uint32><Data Flags> - Requests to join/leave a battle as a spectator
- Data Flags: joins
-
Server->Client: <Data Flags><battleid: uint32><playerid: uint32><# details: {{anc|BattleDetails}} #> - Notifies a player joins/leaves a battle as a spectator
- Data Flags: joins
- Note: The details are present if joins is true, it means you are joining the battle and thus given the details
#####SpectatingBattleMessage
#####SpectatingBattleChat
#####DatabaseMod = 30
#####LoadPlugin
#####Cookie
-
Server->Client: <Network Flags> cookie: string ?> set/delete cookie on client (client must save the cookie based on the IP of the server)
- Network Flags: hasCookie
#####VersionControl
-
Server->Client: <Protocol version: {{anc|ProtocolVersion}}><Data Flags><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}}><server name: string> - 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
- Server->Client: <tierlist:list[uint8, string]> - uint8 is the level of the tier in the hierarchy, string is its name.
- Client->Server: < <teamId: uint8><tier: string>* > - The tier the client wants to change to, for each team.
#####ServMaxChange
#####FindBattle
-
Client->Server: <Network Flags><Data Flags><? Range:uint16 ?><? teams: quint8 ?>
- Network Flags: hasRange, hasTeams
- Data Flags: forceRated, forceSameTier
- Note: If no teams are specified or its value is 0, then it means all teams. If teams are specified, then you test the different bits of it to know which teams are selected
#####ShowRankings
- Server->Client: <tier: string><starting page: uint32><starting rank: uint32><number of pages: uint32><players: list[string, uint16]>
- Client->Server: <tier: string><byPage: bool><#page: uint32#><#name: string#> - Searches ranking, by page or by name
#####Announcement
- Server->Client: <string> - Announcement to display
- Client->Server: <string> - New announcement to display. Valid if user has auth 3 and more
#####CPTBan
#####CPTUnban - 40
#####PlayerTBan
- Server->Client: <dest: uint32, src: uint32, time uint32> - Notifies players about ban. Should not remove player "dest", "time" is in minutes.
- Client->Server: <dest: string><time uint32> - Ask server to ban "time" is in minutes.
#####ShowRankings2
- Server->Client: <mode: uint8><id: uint32><# <count: uint8><<tier: string><rating: uint16><ranking: int32><totalranking: int32> * count> #>
-
Client->Server: <mode: uint8><#id: uint32#> - Searches ranking
- For now only one mode: get all rankings of current teams of a player online
#####BattleList
- Server->Client: <channelid: uint32><battles: list[Battle]>
#####ChannelsList
- Server->Client: <channels: list[uint32, string]>
#####ChannelPlayers
- Server->Client: <channelid: uint32><players: list[uint32]>
#####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
- Server->Client: <channelId: uint32><battle: Battle> - Sends the people in the channel an already ongoing battle. For example if a player battling joins a channel, the people in the channel with receive the event
#####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
#####ServerName
#####SpecialPass
#####ServerListEnd
- Registry->Client: <no data> - Notifies the client that all the server list was sent #####SetIP
#####ServerPass
##Data Structures
###TrainerInfo TrainerInfo is version controlled.
Format: <Network Flags><avatar: uint16><info: string><?<winning message: string><losing message: string><Tie message: string>?>
Network Flags: hasBattleMessages
###Team Team is version controlled.
Format: <NetworkFlags><? defaultTier: string ?><teamGen:generation><? numberOfPokemon:uint8?><pokes:PokePersonal*hasNumberOfPokemon?numberOfPokemon:6>
Network Flags: hasDefaultTier, hasNumberOfPokemon
###PokePersonal PokePersonal is version controlled.
Format: <Network flags><? gen: generation ?><num:pokeid><level:uint8><Data flags><? nick:string ?><? pokeball: uint16 ?><# item:uint16 #><# ability:uint16 #><# nature:uint8 #><# gender:uint8 #><#? happiness:uint8 ?#>< <? pp ups: uint8 ?><move: uint16>*4 ><EV: uint8*6><? IV: uint8*6 ?>
Network flags: hasGen, hasNickname, hasPokeball, 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 Pokémon. 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 Pokémon individually to the server.
###pokeid <num:uint16><formeNum:uint8>
For example, Shaymin-S has num = 492 and formeNum = 1, and Pokémon without formes have formeNum = 0.
###PlayerInfo PlayerInfo is version controlled.
Format:<id:uint32><Network Flags><Data Flags><name:string><color:QColor><avatar:uint16><info:string><auth:int8><numberOfTiers:uint8><(<tier:string><rating:int16>)*numberOfTiers>
Network Flags: none
Data flags: isAway, hasLadderEnabled
The tiers correspond to the different tiers the player is in.
###Battle
Format: <battleId: uint32><Network Flags><mode: Battle Mode><player1: uint32><player2: uint32><? tier: string>
Network Flags: hasTier
Note: currently, there are only 2 ids, but if new battle modes are introduced it could change!
###ChallengeInfo Format: <description:uint8><playerId:int32><clauses:uint32><mode: Battle Mode><team: quint8><? gen: generation ?><srctier: string><desttier:string>
Description tells what kind of information is sent:
- Sent: 0
- Accepted: 1
- Canceled: 2
- Busy: 3
- Refused: 4
- Invalid team: 5
- Invalid gen: 6
- Invalid tier: 7
Clauses are added up from the following array:
- 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
Team
tells the server which team you're using.
DestTier
is the tier you challenge someone to. If you're challenged, you need to send it back as the tier you were challenged to when you accept/refuse
SrcTier
is the tier the challenger challenges from. Only the server needs to send it to the player challenged.
Gen
tells the player challenged in which gen he was challenged.
- Singles: 0
- Doubles: 1
- Triples: 2
- Rotation: 3
- Forfeit: 0
- Win: 1
- Tie: 2
- Close: 3
###BattleConfiguration
BattleConfiguration is version controlled.
Format: <Network Flags><Data Flags><gen: generation><mode: battleMode><clauses:uint32><?numberOfIds:uint8?><ids:uint32*hasNumberOfIds?2:numberOfIds>
Network Flags: hasNumberOfIds
Data Flags: isRated
### <pokemons:PokeBattle*6>
###PokeBattle PokeBattle is version controlled.
Format: <num:pokeid><Data Flags><nick:string><totalHP:uint16><HP:uint16><gender: uint8><level: uint8><item: uint16><ability: uint16><happiness: uint8><stats:uint165><moves: BattleMove4><evs:uint86><ivs:uint86>
Data Flags: isShiny
Note: in the future, a Poké Ball field may be added.
###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
- 5-1: BW2
- 6-0: X/Y
###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.
- No reconnect data - time out: 0
- Wrong secret number: 1
- Not enough command history to catch up: 2
- Wrong IP subrange: 3
###ProtocolVersion
Format: <version number: uint16><subversion number: uint16>
See version control for more info.