/*
SubspaceMobile - A subspace/continuum client for mobile phones
Copyright (C) 2010 Kingsley Masters
Email: kshade2001 at users.sourceforge.net
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
REVISIONS:
*/
package com.subspace.network;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import com.subspace.snrrrub.Checksum;
/**
*
* @author Kingsley
*/
public class NetworkPacket {
// core
public static final String CHARSET = "ISO-8859-1";
public static final byte CORE = 0x00;
public static final byte CORE_CONNECTION = 0x01;
public static final byte CORE_CONNECTIONACK = 0x02;
public static final byte CORE_RELIABLE = 0x03;
public static final byte CORE_RELIABLEACK = 0x04;
public static final byte CORE_SYNC = 0x05;
public static final byte CORE_SYNCACK = 0x06;
public static final byte CORE_DISCONNECT = 0x07;
public static final byte CORE_CHUNK = 0x08;
public static final byte CORE_CHUNKEND = 0x09;
public static final byte CORE_STREAM = 0x0A;
public static final byte CORE_STREAMCANCEL = 0x0B;
public static final byte CORE_STREAMCANCELACK = 0x0C;
public static final byte CORE_CLUSTER = 0x0E;
public static final byte CORE_CTMCONNECTION = 0x10;
public static final byte CORE_CTMCONNECTIONACK = 0x11;
// directory
public static final byte DIRECTORY_REQUEST = 0x01;
public static final byte DIRECTORY_RESPONSE = 0x01;
// game c2s
/* C2S PACKET TYPES */
public static final byte C2S_GOTOARENA = 0x01;
public static final byte C2S_LEAVING =0x02;
public static final byte C2S_POSITION =0x03;
/* missing 04 : appears to be disabled in subgame */
public static final byte C2S_DIE =0x05;
public static final byte C2S_CHAT= 0x06;
public static final byte C2S_GREEN =0x07;
public static final byte C2S_SPECREQUEST= 0x08;
public static final byte C2S_LOGIN= 0x09;
public static final byte C2S_REBROADCAST =0x0A;
public static final byte C2S_UPDATEREQUEST =0x0B;
public static final byte C2S_MAPREQUEST =0x0C;
public static final byte C2S_NEWSREQUEST =0x0D;
public static final byte C2S_RELAYVOICE =0x0E;
public static final byte C2S_SETFREQ =0x0F;
public static final byte C2S_ATTACHTO =0x10;
/* missing 12 : appears to be disabled in subgame */
public static final byte C2S_PICKUPFLAG =0x13;
public static final byte C2S_TURRETKICKOFF =0x14;
public static final byte C2S_DROPFLAGS =0x15;
/* uploading a file to server */
public static final byte C2S_UPLOADFILE =0x16;
public static final byte C2S_REGDATA =0x17;
public static final byte C2S_SETSHIP =0x18;
/* sending new banner */
public static final byte C2S_BANNER =0x19;
public static final byte C2S_SECURITYRESPONSE =0x1A;
public static final byte C2S_CHECKSUMMISMATCH= 0x1B;
public static final byte C2S_BRICK =0x1C;
public static final byte C2S_SETTINGCHANGE =0x1D;
public static final byte C2S_KOTHEXPIRED =0x1E;
public static final byte C2S_SHOOTBALL =0x1F;
public static final byte C2S_PICKUPBALL =0x20;
public static final byte C2S_GOAL =0x21;
/* missing 22 : subspace client sends extra checksums and other security stuff */
/* missing 23 */
public static final byte C2S_CONTLOGIN= 0x24;
public static final byte C2S_DAMAGE= 0x32;
// game s2c
public static final byte S2C_MY_UID = 0x01;
public static final byte S2C_NOW_IN_GAME = 0x02;
public static final byte S2C_PlayerEntering = 0x03;
public static final byte S2C_PlayerLeaving = 0x04;
public static final byte S2C_LargePosition = 0x05;
public static final byte S2C_PlayerDeath = 0x06;
public static final byte S2C_ChatMessage = 0x07;
public static final byte S2C_PlayerGotPrize = 0x08;
public static final byte S2C_PlayerScoreUpdate = 0x09;
public static final byte S2C_PASSWORDACK = 0x0A;
public static final byte S2C_SoccerGoal = 0x0B;
public static final byte S2C_PlayerVoice = 0x0C;
public static final byte S2C_PlayerChangedFreq = 0x0D;
public static final byte S2C_TurretCreate = 0x0E;
public static final byte S2C_ArenaSettings = 0x0F;
public static final byte S2C_FileTransfer = 0x10;
public static final byte S2C_FlagPosition = 0x12;
public static final byte S2C_FlagClaim = 0x13;
public static final byte S2C_FlagVictory = 0x14;
public static final byte S2C_DestroyTurret = 0x15;
public static final byte S2C_FlagDrop = 0x16;
public static final byte S2C_ChecksumRecv = 0x18;
public static final byte S2C_FileRequest = 0x19;
public static final byte S2C_ScoreReset = 0x1A;
public static final byte S2C_YourShipReset = 0x1B;
public static final byte S2C_ShipSpec = 0x1C;
public static final byte S2C_ShipAndFreqChange = 0x1D;
public static final byte S2C_YourBannerChanged = 0x1E;
public static final byte S2C_PlayerBannerChanged = 0x1F;
public static final byte S2C_CollectedPrize = 0x20;
public static final byte S2C_BrickDropped = 0x21;
public static final byte S2C_TurfFlagUpdate = 0x22;
public static final byte S2C_FlagRewardGranted = 0x23;
public static final byte S2C_SpeedGameEnded = 0x24;
public static final byte S2C_ToggleUFO = 0x25;
public static final byte S2C_KeepAlive = 0x27;
public static final byte S2C_SmallPosition = 0x28;
public static final byte S2C_MapInformation = 0x29;
public static final byte S2C_CompressedMapFile = 0x2A;
public static final byte S2C_SetYourKotHTime = 0x2B;
public static final byte S2C_KotHTimerReset = 0x2C;
public static final byte S2C_PowerBallPosition = 0x2E;
public static final byte S2C_ArenaListing = 0x2F;
public static final byte S2C_ZoneBannerAds = 0x30;
public static final byte S2C_PastLogin = 0x31;
public static final byte S2C_ChangeShipPosition = 0x32;
// core packets
static ByteBuffer CreateCore(int length, byte type) {
ByteBuffer bb = ByteBuffer.allocate(length + 2);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(CORE);
bb.put(type);
bb.mark();
return bb;
}
static ByteBuffer CreatePacket(int length, byte type) {
ByteBuffer bb = ByteBuffer.allocate(length + 1);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.put(type);
bb.mark();
return bb;
}
public static ByteBuffer CreateConnection(int key, short version) {
ByteBuffer bb = CreateCore(6, CORE_CONNECTION);
bb.putInt(key);
bb.putShort(version);
return bb;
}
public static ByteBuffer CreateDisconnect() {
return CreateCore(0, CORE_DISCONNECT);
}
public static ByteBuffer CreateReliable(int id, ByteBuffer data) {
data.rewind(); // move data pointer to start
ByteBuffer bb = CreateCore(data.limit() + 4, CORE_RELIABLE);
bb.putInt(id);
bb.put(data);
return bb;
}
public static ByteBuffer CreateReliableAck(int id) {
ByteBuffer bb = CreateCore(4, CORE_RELIABLEACK);
bb.putInt(id);
return bb;
}
public static ByteBuffer CreateSync(int packetInCount, int packetoutCount) {
ByteBuffer bb = CreateCore(14, CORE_SYNC);
bb.putInt(Util.GetTickCount());
bb.putInt(packetoutCount);
bb.putInt(packetInCount);
return bb;
}
public static ByteBuffer CreateSyncResponse(int syncTime) {
ByteBuffer bb = CreateCore(8, CORE_SYNCACK);
bb.putInt(syncTime);
bb.putInt(Util.GetTickCount());
return bb;
}
// directory server packet
public static ByteBuffer CreateZoneListRequest() {
ByteBuffer bb = CreatePacket(4, DIRECTORY_REQUEST);
bb.putShort((short) 0); // min players
bb.putShort((short) 0); // max players
return bb;
}
// game
// <editor-fold defaultstate="collapsed" desc="Packet Description">
/*
* Offset Length Description 0 1 Type byte 1 1 Boolean: New user *1 2 32
* Name 34 32 Password 66 4 Machine ident *2 70 1 ConnectType *3 71 2
* Timezone bias 73 2 ? 75 2 Client version *4 77 4 ? memory checksum, Set
* to = 444 81 4 ? memory checksum, Set to = 555 85 4 Permission ident 89 12
* ?
*
* 1 - 1 = New, 0 = Not New
*
* 2 - Should be Drive Seriel of C (can be random for bots)
*
* 3 - Type 0x00 is a safe bet.
*
* 4 - 0x24 = Ctm, 0x86 = SS
*/
// </editor-fold>
public static ByteBuffer CreatePassword(boolean newUser, String name,
String password, int macId) {
try {
int permissionId = (Util.GetTickCount() ^ 0xAAAAAAAA) * 0x5f346d + 0x5abcdef;
// create buffer
ByteBuffer bb = CreatePacket(100, C2S_CONTLOGIN);
bb.put((byte) (newUser ? 0x01 : 0x00));
bb.position(2);
bb.put(name.getBytes(CHARSET));
bb.position(34);
bb.put(password.getBytes(CHARSET));
bb.putInt(66, macId);
bb.put(70, (byte) 0x00); // conection type
bb.putShort(71, (short) 240); // timezone bias (240=est)
bb.put(73, (byte) 0x00); // unknown
bb.putShort(75, (short) 0x86); // type = VIE
bb.putInt(77, 444); // memory checksum
bb.putInt(81, 555); // memory checksum
bb.putInt(85, permissionId); // permissionid
return bb;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// <editor-fold defaultstate="collapsed" desc=" Packet Description ">
/*
* Offset Length Descriptoin
* 0 1 Type Byte 0x01
* 1 1 Ship type
* 2 2 Allow audio?
* 4 2 X resolution
* 6 2 Y resolution
* 8 2 Main arena number *1
*
* 10 16 Arena name (Optional)
*
* 1 - Set to 0xFFFF for Random Pub, or 0xFFFD for Specific Sub (Must
* provide name).
*
* //ship types
*
* WARBIRD = 0 JAVELIN = 1 SPIDER = 2 LEVIATHAN = 3 TERRIER = 4 WEASEL = 5
* LANCASTER = 6 SHARK = 7 SPECTATOR = 8
*/
// </editor-fold>
public static ByteBuffer CreateArenaLogin(byte shiptype, short xres,
short yres, String arena, byte optionalLVZ) {
try {
ByteBuffer bb;
bb = CreatePacket(25, C2S_GOTOARENA);
bb.put(1, shiptype);
bb.putShort(2, (short) 0); // disable sound
bb.putShort(4, xres);
bb.putShort(6, yres);
if (arena == null || arena.length() == 0) {
bb.putShort(8, (short) 0xFFFF);
} else {
bb.putShort(8, (short) 0xFFFD);
}
if (arena != null) {
bb.position(10);
bb.put(arena.getBytes(CHARSET));
}
bb.put(24,optionalLVZ);
return bb;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static ByteBuffer CreateMapRequest() {
ByteBuffer bb = CreatePacket(0, C2S_MAPREQUEST);
return bb;
}
/*
* 0x03 Position packet
*
* Offset Length Description
* 0 1 Type Byte 0x03
* 1 1 Direction (0 ... 360) *
* 2 4 Timestamp
* 6 2 X Velocity
* 8 2 Y Pixels (0 ... 16384)
* 10 1 Checksum
* 11 1 * Togglables *1
* 12 2 X Pixels (0 ... 16384)
* 14 2 Y Velocity
* 16 2 Bounty
* 18 2 Energy
* 20 2 Weapon Info *2
*
* 22 2 Energy *4 (Optional) 24 2 S2C Latency *4 (Optional) 26 2 Timer *4
* (Optional) 28 4 Item info *3 *4 (Optional)
*
* 1 - Togglables: Each value is one bit in a byte Bit 1 - Stealth Bit 2 -
* Cloak Bit 4 - XRadar Bit 8 - Antiwarp Bit 16 - Flash (Play the cloak/warp
* flash) Bit 32 - Safety (In safe) Bit 64 - UFO (Using UFO) Bit 128 - ?
*
* 2 - Weapon info: Unsigned Integer Values or Booleans
*
* Weapon type :5 bits *a1 Weapon level :2 bits Bouncing (Boolean) :1 bit
* EMP (Boolean) :1 bit Is bomb (Boolean) :1 bit Shrapnel :5 bits Alternate
* (Boolean) :1 bit *a2
*
*
* a1 - Weapon types: 0x00 - None 0x01 - Bullet 0x02 - Bouncing bullet 0x03
* - Bomb 0x04 - Proximity bomb 0x05 - Repel 0x06 - Decoy 0x07 - Burst 0x08
* - Thor
*
* a2 - (Bombs -> Mines; Bullets -> Multifire)
*
* 3 - Item info: Unsigned Integer Values or Booleans
*
* Shields (Boolean) :1 bit Super (Boolean) :1 bit Burst Count :4 bits Repel
* Count :4 bits Thor Count :4 bits Brick Count :4 bits Decoy Count :4 bits
* Rocket Count :4 bits Portal Count :4 bits ? Unknown :2 bits
*
* 4 - These are supposed to be optional, but I'm not certain what dictates
* if you should send them or not.
*/
public static ByteBuffer CreatePosition(short xPos, short yPos,
byte direction, short xV, short yV, short bounty, short energy,
short weapon, byte togglables) {
ByteBuffer bb = CreatePacket(21, C2S_POSITION);
byte checksum = 0;
bb.put(1, direction);
bb.putInt(2, Util.GetTickCount()); // timestamp
bb.putShort(6, xV);
bb.putShort(8, yPos);
bb.put(11, togglables);
bb.putShort(12, xPos);
bb.putShort(14, yV);
bb.putShort(16, bounty);
bb.putShort(18, energy);
bb.putShort(20, weapon);
// now add checksum
for (int i = 0; i < 22; i++) {
checksum ^= bb.get(i);
}
bb.put(10, checksum);// checksum
// TODO Auto-generated method stub
return bb;
}
/*
* 0x1A Security checksum *1
*
* Offset Length Description 0 1 Type Byte 0x1A 1 4 Weapon Count 5 4
* Settings Checksum *2 9 4 Subspace.EXE Checksum 13 4 Map.LVL Checksum 17 4
* S2CSlowTotal 21 4 S2CFastTotal 25 2 S2CSlowCurrent 27 2 S2CFastCurrent 29
* 2 S2CRelOut (?Unsure?) 31 2 Ping 33 2 Ping Average 35 2 Ping Low 37 2
* Ping High 39 1 Slow Frame Detected (Boolean)
*
* 1 - The checksums are generated after a server-sent seed
*
* 2 - Settings checksum is the checksum of the contents of the settings
* packet
*/
public static ByteBuffer CreateSecurityChecksum(int settingsChecksum,
int exeChecksum, int lvlChecksum) {
ByteBuffer bb = CreatePacket(40, C2S_SECURITYRESPONSE);
// generate checksums
bb.putInt(5, settingsChecksum);
bb.putInt(9, exeChecksum);
bb.putInt(13, lvlChecksum);
return bb;
}
public static ByteBuffer CreateNewsTxtRequest() {
ByteBuffer bb = CreatePacket(0, C2S_NEWSREQUEST);
return bb;
}
/*
* Offset Length Description 0 1 Type Byte 0x06 1 1 Chat Type *1 2 1 Sound
* Byte 3 2 Target Player's Player ID *2 5 * Text...
*
* 1 - Chat types: 0x00 - Message in green text [*arena, *zone, ...] 0x01 -
* Public macro
*
* 0x02 - Public message
*
* 0x03 - Team message [// or '] 0x04 - Player to all members of another
* team ["Whatever] 0x05 - Private message [/Whatever] 0x06 - Red warning
* message [MODERATOR WARNING: Whatever -Whoever] 0x07 - Remote private
* message *3 [(Whoever)> Whatever] 0x08 - Red server errors, without a name
* tag (S2C only) 0x09 - Channel message *3 [X;Whatever]
*
* 2 - Target player's player ID is 0x00 for the Following Chat Types: 0x00,
* 0x01, 0x02, 0x03, 0x06, 0x07, 0x08 0x09. It is PlayerID for 0x05 and 0x04
*
* 3 - These are formatted much as you�d expect. To send a Remote Message,
* you include the name of the person you are sending the message to in the
* Text of the message like so: ":Name:Message" and with Channel Messages
* you include the Number of the Channel you are sending to, like so:
* "#;Message".
*/
public static ByteBuffer CreateChat(byte chatType, byte sound,
short targetPlayer, String text) {
try {
ByteBuffer bb = CreatePacket(text.length() + 5, C2S_CHAT);
bb.put(1, chatType);
bb.put(2, sound);
bb.putShort(3, targetPlayer);
bb.position(5);
bb.put(text.getBytes(CHARSET));
return bb;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}