Skip to content

Commit 8a1b34f

Browse files
committed
Add more info to readme and add various fixes
- Set to VIE encryption by default so the client is easier to use. - Don't increase bounty if target killed was 0 bounty. - Add timer drift and map crc32 to security packet with Continuum connection. - Fix some prize seed mutations. - Fix downloading level screen with vie connections. - Fix ship reset on warp prize.
1 parent 36c3b58 commit 8a1b34f

File tree

9 files changed

+116
-12
lines changed

9 files changed

+116
-12
lines changed

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ SubSpace client that I will probably never finish.
55
The client is designed to be a drop-in replacement client for Continuum. Build the client and drop it into the Continuum folder.
66
Tested on modern Windows 10 and Linux setup, 12 year old Windows 10 laptop, and Android phone.
77

8+
A built VIE-only client for Windows can be downloaded in the [Release](https://github.com/plushmonkey/nullspace/releases) section.
9+
The local server is set to `127.0.0.1:5000` and subgame is set to `127.0.0.1:5002`. You need to compile your own version to have these changed.
10+
11+
#### Security
12+
Solving the checksum and key expansion requests is done by using a private network service. There's no public server available yet.
13+
The security service needs to remain private to maintain integrity of the game. It would be too easy to cheat / attack servers if it was released.
14+
15+
This means that the client cannot connect to any Continuum-only zones by default.
16+
The zones will need to have VIE encryption enabled. Check the documentation of the server to learn how to enable this.
17+
818
### Video
919
Example video showcasing the client in Hyperspace, Extreme Games, and a local server with Trench Wars settings.
1020

@@ -38,15 +48,14 @@ Example video showcasing the client in Hyperspace, Extreme Games, and a local se
3848
- Flag pickup and flag turf claiming
3949
- Balls
4050

41-
## Security
42-
Solving the checksum and key expansion requests is done by using a private network service. There's no public server available yet.
43-
4451
## Building
4552
### Getting source
4653
1. `git clone https://github.com/plushmonkey/nullspace`
4754
2. `cd nullspace`
4855
3. `git submodule init && git submodule update`
4956

57+
Most of the client configuration can be done in `main.cpp`. The `InitialSettings` function and the `kServers` array are the main sections that might need to be changed.
58+
5059
### Windows
5160
Use the existing Visual Studio solution or install cmake. Choose Release x64 build.
5261

@@ -59,4 +68,5 @@ Use the existing Visual Studio solution or install cmake. Choose Release x64 bui
5968
6. `make`
6069

6170
### Android
62-
Use the provided gradlew in the android folder.
71+
Use the provided gradlew in the android folder.
72+
This version doesn't support input and isn't really maintained, so it might not compile.

src/null/Game.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,10 @@ static void OnPlayerPrizePkt(void* user, u8* pkt, size_t size) {
228228
u16 share_limit = game->connection.settings.ShipSettings[self->ship].PrizeShareLimit;
229229

230230
if (self->bounty < share_limit) {
231+
u32 pristine_seed = game->connection.security.prize_seed;
232+
231233
game->ship_controller.ApplyPrize(self, prize_id, true);
234+
game->connection.security.prize_seed = pristine_seed;
232235
}
233236
}
234237
}
@@ -731,10 +734,14 @@ void Game::RenderJoin(float dt) {
731734
}
732735
} break;
733736
case Connection::LoginState::MapDownload: {
734-
int percent = (int)(connection.packet_sequencer.huge_chunks.size * 100 / (float)connection.map.compressed_size);
735737
char downloading[64];
736738

737-
sprintf(downloading, "Downloading level: %d%%", percent);
739+
if (connection.map.compressed_size > 0) {
740+
int percent = (int)(connection.packet_sequencer.huge_chunks.size * 100 / (float)connection.map.compressed_size);
741+
sprintf(downloading, "Downloading level: %d%%", percent);
742+
} else {
743+
sprintf(downloading, "Downloading level: %d bytes", (int)(connection.packet_sequencer.huge_chunks.size));
744+
}
738745

739746
Vector2f download_pos(ui_camera.surface_dim.x * 0.5f, ui_camera.surface_dim.y * 0.8f);
740747

src/null/PlayerManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ void PlayerManager::OnPlayerDeath(u8* pkt, size_t size) {
638638
killer->flag_timer = connection.settings.FlagDropDelay;
639639
}
640640

641-
if (killer->id == player_id) {
641+
if (killer->id == player_id && killed && killed->bounty > 0) {
642642
killer->bounty += connection.settings.BountyIncreaseForKill;
643643
}
644644
}

src/null/ShipController.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1074,9 +1074,13 @@ void ShipController::OnCollectedPrize(u8* pkt, size_t size) {
10741074

10751075
player_manager.sound_system.Play(AudioType::Prize);
10761076

1077+
u32 pristine_seed = player_manager.connection.security.prize_seed;
1078+
10771079
for (u16 i = 0; i < count; ++i) {
10781080
ApplyPrize(self, prize_id, true);
10791081
}
1082+
1083+
player_manager.connection.security.prize_seed = pristine_seed;
10801084
}
10811085

10821086
void ShipController::ApplyPrize(Player* self, s32 prize_id, bool notify, bool damage) {
@@ -1162,6 +1166,28 @@ void ShipController::ApplyPrize(Player* self, s32 prize_id, bool notify, bool da
11621166
bool display_notification = false;
11631167

11641168
switch (prize) {
1169+
case Prize::None: {
1170+
u32 pristine_seed = player_manager.connection.security.prize_seed;
1171+
1172+
for (u16 attempts = 0; attempts < 9999; ++attempts) {
1173+
s32 random_prize = GeneratePrize(false);
1174+
1175+
if (random_prize == 0 || random_prize == (s32)Prize::EngineShutdown || random_prize == (s32)Prize::Shields ||
1176+
random_prize == (s32)Prize::Super || random_prize == (s32)Prize::Multiprize ||
1177+
random_prize == (s32)Prize::Warp) {
1178+
continue;
1179+
}
1180+
1181+
u16 bounty = self->bounty;
1182+
ApplyPrize(self, random_prize, false, false);
1183+
self->bounty = bounty;
1184+
break;
1185+
}
1186+
1187+
player_manager.connection.security.prize_seed = pristine_seed;
1188+
1189+
display_notification = false;
1190+
} break;
11651191
case Prize::Recharge: {
11661192
display_notification = true;
11671193

@@ -1273,7 +1299,7 @@ void ShipController::ApplyPrize(Player* self, s32 prize_id, bool notify, bool da
12731299
} break;
12741300
case Prize::Warp: {
12751301
display_notification = true;
1276-
player_manager.Spawn();
1302+
player_manager.Spawn(false);
12771303
self->velocity = Vector2f(0, 0);
12781304
} break;
12791305
case Prize::Guns: {
@@ -1544,6 +1570,8 @@ void ShipController::ApplyPrize(Player* self, s32 prize_id, bool notify, bool da
15441570
if (!negative) {
15451571
u16 count = player_manager.connection.settings.MultiPrizeCount;
15461572

1573+
u32 pristine_seed = player_manager.connection.security.prize_seed;
1574+
15471575
size_t attempts = 0;
15481576
for (u16 i = 0; i < count && attempts < 9999; ++i, ++attempts) {
15491577
s32 random_prize = GeneratePrize(false);
@@ -1560,6 +1588,8 @@ void ShipController::ApplyPrize(Player* self, s32 prize_id, bool notify, bool da
15601588
self->bounty = bounty;
15611589
}
15621590

1591+
player_manager.connection.security.prize_seed = pristine_seed;
1592+
15631593
display_notification = true;
15641594
}
15651595
} break;

src/null/WorkQueue.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <null/Memory.h>
55
#include <null/Types.h>
66
//
7+
#include <atomic>
78
#include <condition_variable>
89
#include <mutex>
910
#include <thread>
@@ -28,7 +29,7 @@ struct Work {
2829

2930
struct WorkQueue {
3031
MemoryArena& arena;
31-
volatile size_t queue_size;
32+
std::atomic<u32> queue_size;
3233
Work* queue;
3334

3435
Work* free;

src/null/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void InitializeSettings() {
5555
g_Settings.window_type = WindowType::Windowed;
5656
g_Settings.render_stars = true;
5757

58-
g_Settings.encrypt_method = EncryptMethod::Continuum;
58+
g_Settings.encrypt_method = EncryptMethod::Subspace;
5959

6060
g_Settings.sound_enabled = true;
6161
g_Settings.sound_volume = 0.15f;

src/null/net/Connection.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ void Connection::ProcessPacket(u8* pkt, size_t size) {
297297
} sync_response = {0x00, 0x06, timestamp, GetCurrentTick()};
298298
#pragma pack(pop)
299299

300-
packet_sequencer.SendReliableMessage(*this, (u8*)&sync_response, sizeof(sync_response));
300+
Send((u8*)&sync_response, sizeof(sync_response));
301301

302302
last_sync_tick = GetCurrentTick();
303303
} break;
@@ -589,6 +589,8 @@ void Connection::ProcessPacket(u8* pkt, size_t size) {
589589

590590
if (encrypt_method == EncryptMethod::Continuum) {
591591
map.compressed_size = buffer.ReadU32();
592+
} else {
593+
map.compressed_size = 0;
592594
}
593595

594596
char filename[17];
@@ -746,7 +748,15 @@ void Connection::SendSecurity(u32 settings_checksum, u32 exe_checksum, u32 map_c
746748
buffer.WriteU16(stat.ping_avg / 10); // Ping average
747749
buffer.WriteU16(stat.ping_low / 10); // Ping low
748750
buffer.WriteU16(stat.ping_high / 10); // Ping high
749-
buffer.WriteU8(0); // slow frame
751+
buffer.WriteU8(0); // Slow frame
752+
753+
if (encrypt_method == EncryptMethod::Continuum) {
754+
buffer.WriteU16(0); // Timer drift
755+
756+
u32 map_crc32 = crc32_map(map.tiles, 1024 * 1024);
757+
758+
buffer.WriteU32(map_crc32);
759+
}
750760

751761
packet_sequencer.SendReliableMessage(*this, buffer.data, buffer.GetSize());
752762
}

src/null/net/security/Checksum.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,51 @@ u32 crc32(const u8* ptr, size_t size) {
139139
return crc;
140140
}
141141

142+
static const uint32_t g_crc32_reflected_table[256] = {
143+
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832,
144+
0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
145+
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a,
146+
0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
147+
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
148+
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
149+
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab,
150+
0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
151+
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4,
152+
0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
153+
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074,
154+
0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
155+
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525,
156+
0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
157+
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
158+
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
159+
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76,
160+
0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
161+
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6,
162+
0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
163+
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7,
164+
0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
165+
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7,
166+
0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
167+
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
168+
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
169+
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330,
170+
0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
171+
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
172+
173+
u32 crc32_map(const u8* ptr, size_t size) {
174+
u32 crc = 0xFFFFFFFF;
175+
176+
for (size_t i = 0; i < size; ++i) {
177+
u8 tile = ptr[i];
178+
179+
if (tile != 0 && (tile < 0xA1 || tile == 0xAB)) {
180+
crc = (crc >> 8) ^ g_crc32_reflected_table[crc & 0xFF ^ (u32)tile];
181+
}
182+
}
183+
184+
return crc;
185+
}
186+
142187
u32 SettingsChecksum(u32 key, const ArenaSettings& settings) {
143188
u32* data = (u32*)&settings;
144189
u32 sum = 0;

src/null/net/security/Checksum.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace null {
77

88
u8 crc8(const u8* ptr, size_t len);
99
u32 crc32(const u8* ptr, size_t size);
10+
u32 crc32_map(const u8* ptr, size_t size);
1011

1112
struct ArenaSettings;
1213
u32 SettingsChecksum(u32 key, const ArenaSettings& settings);

0 commit comments

Comments
 (0)