delete old pp layer

This commit is contained in:
jacob 2026-01-07 02:57:59 -06:00
parent 254c6c4126
commit 5bbf007fec
43 changed files with 218 additions and 11304 deletions

View File

@ -36,14 +36,14 @@ N_Host *N_AcquireHost(u16 listen_port)
host->num_msg_assembler_lookup_bins = N_NumMsgAssemblerLookupBins;
host->msg_assembler_lookup_bins = PushStructs(host->arena, N_MsgAssemblerLookupBin, host->num_msg_assembler_lookup_bins);
host->sock = P_AcquireSock(listen_port, Mebi(2), Mebi(2));
host->sock = PLT_AcquireSock(listen_port, Mebi(2), Mebi(2));
return host;
}
void N_ReleaseHost(N_Host *host)
{
P_ReleaseSock(host->sock);
PLT_ReleaseSock(host->sock);
ReleaseBuddyCtx(host->buddy);
ReleaseArena(host->rcv_buffer_write->arena);
@ -56,18 +56,18 @@ void N_ReleaseHost(N_Host *host)
////////////////////////////////////////////////////////////
//~ Channel
u64 N_HashFromAddress(P_Address address)
u64 N_HashFromAddress(PLT_Address address)
{
return HashFnv64(Fnv64Basis, StringFromStruct(&address));
}
N_Channel *N_ChannelFromAddress(N_Host *host, P_Address address)
N_Channel *N_ChannelFromAddress(N_Host *host, PLT_Address address)
{
u64 hash = N_HashFromAddress(address);
N_ChannelLookupBin *bin = &host->channel_lookup_bins[hash % host->num_channel_lookup_bins];
for (N_Channel *channel = bin->first; channel; channel = channel->next_address_hash)
{
if (channel->address_hash == hash && P_MatchAddress(channel->address, address))
if (channel->address_hash == hash && PLT_MatchAddress(channel->address, address))
{
return channel;
}
@ -127,7 +127,7 @@ N_ChannelList N_ChannelsFromId(Arena *arena, N_Host *host, N_ChannelId channel_i
return result;
}
N_Channel *N_AcquireChannel(N_Host *host, P_Address address)
N_Channel *N_AcquireChannel(N_Host *host, PLT_Address address)
{
N_ChannelId id = ZI;
N_Channel *channel;
@ -498,7 +498,7 @@ N_Cmd *N_PushCmd(N_Host *host)
return cmd;
}
void N_Connect(N_Host *host, P_Address connect_address)
void N_Connect(N_Host *host, PLT_Address connect_address)
{
N_Channel *channel = N_ChannelFromAddress(host, connect_address);
if (!channel->valid)
@ -550,11 +550,11 @@ N_EventList N_BeginUpdate(Arena *arena, N_Host *host)
N_RcvPacket *first_packet = 0;
N_RcvPacket *last_packet = 0;
{
P_Sock *sock = host->sock;
P_SockReadResult result = ZI;
while ((result = P_ReadSock(scratch.arena, sock)).valid)
PLT_Sock *sock = host->sock;
PLT_SockReadResult result = ZI;
while ((result = PLT_ReadSock(scratch.arena, sock)).valid)
{
P_Address address = result.address;
PLT_Address address = result.address;
String data = result.data;
if (data.len > 0)
{
@ -581,7 +581,7 @@ N_EventList N_BeginUpdate(Arena *arena, N_Host *host)
for (N_RcvPacket *packet = first_packet; packet; packet = packet->next)
{
//struct sock *sock = packet->sock;
P_Address address = packet->address;
PLT_Address address = packet->address;
BB_Buff bb = BB_BuffFromString(packet->data);
BB_Reader br = BB_ReaderFromBuff(&bb);
u32 magic = BB_ReadUBits(&br, 32); // TODO: implicitly encode magic into crc32
@ -634,7 +634,7 @@ N_EventList N_BeginUpdate(Arena *arena, N_Host *host)
// A foreign host is trying to connect to us
if (!channel->valid)
{
LogInfoF("Host received conection attempt from %F", FmtString(P_StringFromAddress(scratch.arena, address)));
LogInfoF("Host received conection attempt from %F", FmtString(PLT_StringFromAddress(scratch.arena, address)));
// TODO: Verify that some per-host uuid isn't present in a rolling window to prevent reconnects right after a disconnect?
channel = N_AcquireChannel(host, address);
}
@ -651,7 +651,7 @@ N_EventList N_BeginUpdate(Arena *arena, N_Host *host)
// We successfully connected to a foreign host and they are ready to receive messages
if (channel->valid && !channel->connected)
{
LogInfoF("Host received connection from %F", FmtString(P_StringFromAddress(scratch.arena, address)));
LogInfoF("Host received connection from %F", FmtString(PLT_StringFromAddress(scratch.arena, address)));
N_Event *event = N_PushEvent(arena, &events);
event->kind = N_EventKind_ChannelOpened;
event->channel_id = channel->id;
@ -667,7 +667,7 @@ N_EventList N_BeginUpdate(Arena *arena, N_Host *host)
// A foreign host disconnected from us
if (channel->valid)
{
LogInfoF("Host received disconnection from %F", FmtString(P_StringFromAddress(scratch.arena, address)));
LogInfoF("Host received disconnection from %F", FmtString(PLT_StringFromAddress(scratch.arena, address)));
N_Event *event = N_PushEvent(arena, &events);
event->kind = N_EventKind_ChannelClosed;
event->channel_id = channel->id;
@ -1014,22 +1014,22 @@ void N_EndUpdate(N_Host *host)
{
for (u64 i = 0; i < host->num_channels_reserved; ++i)
{
P_Sock *sock = host->sock;
PLT_Sock *sock = host->sock;
N_Channel *channel = &host->channels[i];
u64 total_sent = 0;
if (channel->valid)
{
P_Address address = channel->address;
PLT_Address address = channel->address;
// Send reliable packets to channel
for (N_SndPacket *packet = channel->first_reliable_packet; packet; packet = packet->next)
{
P_WriteSock(sock, address, STRING(packet->data_len, packet->data));
PLT_WriteSock(sock, address, STRING(packet->data_len, packet->data));
total_sent += packet->data_len;
}
// Send unreliable packets to channel
for (N_SndPacket *packet = channel->first_unreliable_packet; packet; packet = packet->next)
{
P_WriteSock(sock, address, STRING(packet->data_len, packet->data));
PLT_WriteSock(sock, address, STRING(packet->data_len, packet->data));
total_sent += packet->data_len;
}
// Release unreliable packets

View File

@ -106,8 +106,8 @@ Struct(N_SndPacket)
Struct(N_RcvPacket)
{
P_Sock *sock;
P_Address address;
PLT_Sock *sock;
PLT_Address address;
String data;
N_RcvPacket *next;
};
@ -131,7 +131,7 @@ Struct(N_Channel)
N_Channel *next_free;
P_Address address;
PLT_Address address;
u64 address_hash;
N_Channel *next_address_hash;
N_Channel *prev_address_hash;
@ -222,7 +222,7 @@ Struct(N_Host)
{
Arena *arena;
P_Sock *sock;
PLT_Sock *sock;
BuddyCtx *buddy; // For storing msg assembler data
@ -274,11 +274,11 @@ void N_ReleaseHost(N_Host *host);
#define N_MatchChannelId(a, b) ((a).gen == (b).gen && (a).idx == (b).idx)
#define N_IsChannelIdNil(v) ((v).gen == 0 && (v).idx == 0)
u64 N_HashFromAddress(P_Address address);
N_Channel *N_ChannelFromAddress(N_Host *host, P_Address address);
u64 N_HashFromAddress(PLT_Address address);
N_Channel *N_ChannelFromAddress(N_Host *host, PLT_Address address);
N_Channel *N_ChannelFromId(N_Host *host, N_ChannelId channel_id);
N_ChannelList N_ChannelsFromId(Arena *arena, N_Host *host, N_ChannelId channel_id);
N_Channel *N_AcquireChannel(N_Host *host, P_Address address);
N_Channel *N_AcquireChannel(N_Host *host, PLT_Address address);
void N_ReleaseChannel(N_Channel *channel);
////////////////////////////////////////////////////////////
@ -301,7 +301,7 @@ N_SndPacket *N_PushSndPacket(N_Channel *channel, b32 is_reliable);
//~ Host command
N_Cmd *N_PushCmd(N_Host *host);
void N_Connect(N_Host *host, P_Address connect_address);
void N_Connect(N_Host *host, PLT_Address connect_address);
void N_Disconnect(N_Host *host, N_ChannelId channel_id);
void N_Write(N_Host *host, N_ChannelId channel_id, String msg, N_WriteFlag flags);

View File

@ -1,26 +1,26 @@
////////////////////////////////////////////////////////////
//~ Opaque types
Struct(P_Watch);
Struct(P_Sock);
Struct(PLT_Watch);
Struct(PLT_Sock);
////////////////////////////////////////////////////////////
//~ File system types
Struct(P_File)
Struct(PLT_File)
{
u64 handle;
b32 valid;
};
Struct(P_FileTime)
Struct(PLT_FileTime)
{
DateTime created;
DateTime accessed;
DateTime modified;
};
Struct(P_FileMap)
Struct(PLT_FileMap)
{
String mapped_memory;
u64 handle;
@ -30,16 +30,16 @@ Struct(P_FileMap)
////////////////////////////////////////////////////////////
//~ Address types
Enum(P_AddressFamily)
Enum(PLT_AddressFamily)
{
P_AddressFamily_Ipv4,
P_AddressFamily_Ipv6
PLT_AddressFamily_Ipv4,
PLT_AddressFamily_Ipv6
};
Struct(P_Address)
Struct(PLT_Address)
{
b32 valid;
P_AddressFamily family;
PLT_AddressFamily family;
// NOTE: ipnb & portnb are stored in network byte order
u8 ipnb[16];
u16 portnb;
@ -48,28 +48,28 @@ Struct(P_Address)
////////////////////////////////////////////////////////////
//~ Sock types
Struct(P_SockReadResult)
Struct(PLT_SockReadResult)
{
b32 valid; // Since data.len = 0 can be valid
P_Address address;
PLT_Address address;
String data;
};
////////////////////////////////////////////////////////////
//~ Message box types
Enum(P_MessageBoxKind)
Enum(PLT_MessageBoxKind)
{
P_MessageBoxKind_Ok,
P_MessageBoxKind_Warning,
P_MessageBoxKind_Error,
P_MessageBoxKind_Fatal
PLT_MessageBoxKind_Ok,
PLT_MessageBoxKind_Warning,
PLT_MessageBoxKind_Error,
PLT_MessageBoxKind_Fatal
};
////////////////////////////////////////////////////////////
//~ @hookdecl Bootstrap
void P_Bootstrap(void);
void PLT_Bootstrap(void);
////////////////////////////////////////////////////////////
//~ @hookdecl File system
@ -77,65 +77,65 @@ void P_Bootstrap(void);
// NOTE: File paths use forward slash '/' as delimiter
//- File system helpers
String P_GetWritePath(Arena *arena);
b32 P_IsFile(String path);
b32 P_IsDir(String path);
void P_MkDir(String path);
String PLT_GetWritePath(Arena *arena);
b32 PLT_IsFile(String path);
b32 PLT_IsDir(String path);
void PLT_MkDir(String path);
//- File creation
P_File P_OpenFileRead(String path);
P_File P_OpenFileReadWait(String path); // Waits until file is not being used by another program
P_File P_OpenFileWrite(String path);
P_File P_OpenFileAppend(String path);
void P_CloseFile(P_File file);
PLT_File PLT_OpenFileRead(String path);
PLT_File PLT_OpenFileReadWait(String path); // Waits until file is not being used by another program
PLT_File PLT_OpenFileWrite(String path);
PLT_File PLT_OpenFileAppend(String path);
void PLT_CloseFile(PLT_File file);
//- File data manipulation
String P_ReadFile(Arena *arena, P_File file);
void P_WriteFile(P_File file, String data);
String PLT_ReadFile(Arena *arena, PLT_File file);
void PLT_WriteFile(PLT_File file, String data);
//- File info
u64 P_GetFileSize(P_File file);
P_FileTime P_GetFileTime(P_File file);
u64 PLT_GetFileSize(PLT_File file);
PLT_FileTime PLT_GetFileTime(PLT_File file);
////////////////////////////////////////////////////////////
//~ @hookdecl File map
P_FileMap P_OpenFileMap(P_File file);
void P_CloseFileMap(P_FileMap map);
String P_GetFileMapData(P_FileMap map);
PLT_FileMap PLT_OpenFileMap(PLT_File file);
void PLT_CloseFileMap(PLT_FileMap map);
String PLT_GetFileMapData(PLT_FileMap map);
////////////////////////////////////////////////////////////
//~ @hookdecl Address helpers
P_Address P_AddressFromString(String str);
P_Address P_AddressFromIpPortCstr(char *ip_cstr, char *port_cstr);
P_Address P_AddressFromPort(u16 port);
String P_StringFromAddress(Arena *arena, P_Address address);
b32 P_MatchAddress(P_Address a, P_Address b);
PLT_Address PLT_AddressFromString(String str);
PLT_Address PLT_AddressFromIpPortCstr(char *ip_cstr, char *port_cstr);
PLT_Address PLT_AddressFromPort(u16 port);
String PLT_StringFromAddress(Arena *arena, PLT_Address address);
b32 PLT_MatchAddress(PLT_Address a, PLT_Address b);
////////////////////////////////////////////////////////////
//~ @hookdecl Socket
P_Sock *P_AcquireSock(u16 listen_port, u64 sndbuf_size, u64 rcvbuf_size);
void P_ReleaseSock(P_Sock *sock);
P_SockReadResult P_ReadSock(Arena *arena, P_Sock *sock);
void P_WriteSock(P_Sock *sock, P_Address address, String data);
PLT_Sock *PLT_AcquireSock(u16 listen_port, u64 sndbuf_size, u64 rcvbuf_size);
void PLT_ReleaseSock(PLT_Sock *sock);
PLT_SockReadResult PLT_ReadSock(Arena *arena, PLT_Sock *sock);
void PLT_WriteSock(PLT_Sock *sock, PLT_Address address, String data);
////////////////////////////////////////////////////////////
//~ @hookdecl Utils
void P_MessageBox(P_MessageBoxKind kind, String message);
void P_SetClipboardText(String str);
String P_GetClipboardText(Arena *arena);
void PLT_MessageBox(PLT_MessageBoxKind kind, String message);
void PLT_SetClipboardText(String str);
String PLT_GetClipboardText(Arena *arena);
////////////////////////////////////////////////////////////
//~ @hookdecl Timer
Fence *P_GetTimerFence(void);
i64 P_GetCurrentTimerPeriodNs(void);
Fence *PLT_GetTimerFence(void);
i64 PLT_GetCurrentTimerPeriodNs(void);
////////////////////////////////////////////////////////////
//~ @hookdecl Sleep
void P_SleepPrecise(i64 sleep_time_ns);
void P_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns);
void PLT_SleepPrecise(i64 sleep_time_ns);
void PLT_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns);

View File

@ -5,7 +5,7 @@
@IncludeC platform.h
@Bootstrap P_Bootstrap
@Bootstrap PLT_Bootstrap
//////////////////////////////
//- Impl

View File

@ -1,25 +1,25 @@
P_W32_Ctx P_W32 = Zi;
PLT_W32_Ctx PLT_W32 = Zi;
////////////////////////////////////////////////////////////
//~ @hookimpl Bootstrap
void P_Bootstrap(void)
void PLT_Bootstrap(void)
{
//- Init watches pool
P_W32.watches_arena = AcquireArena(Gibi(64));
PLT_W32.watches_arena = AcquireArena(Gibi(64));
// Init winsock
WSAStartup(MAKEWORD(2, 2), &P_W32.wsa_data);
P_W32.socks_arena = AcquireArena(Gibi(64));
WSAStartup(MAKEWORD(2, 2), &PLT_W32.wsa_data);
PLT_W32.socks_arena = AcquireArena(Gibi(64));
// Init timer
DispatchWave(Lit("Win32 timer sync"), 1, P_W32_SyncTimerForever, 0);
DispatchWave(Lit("Win32 timer sync"), 1, PLT_W32_SyncTimerForever, 0);
}
////////////////////////////////////////////////////////////
//~ Time
DateTime P_W32_DateTimeFromWin32SystemTime(SYSTEMTIME st)
DateTime PLT_W32_DateTimeFromWin32SystemTime(SYSTEMTIME st)
{
return (DateTime)
{
@ -37,7 +37,7 @@ DateTime P_W32_DateTimeFromWin32SystemTime(SYSTEMTIME st)
////////////////////////////////////////////////////////////
//~ File system
String P_W32_StringFromWin32Path(Arena *arena, wchar_t *src)
String PLT_W32_StringFromWin32Path(Arena *arena, wchar_t *src)
{
String result = Zi;
result.text = ArenaNext(arena, u8);
@ -65,10 +65,10 @@ String P_W32_StringFromWin32Path(Arena *arena, wchar_t *src)
////////////////////////////////////////////////////////////
//~ Address
P_W32_Address P_W32_Win32AddressFromPlatformAddress(P_Address addr)
PLT_W32_Address PLT_W32_Win32AddressFromPlatformAddress(PLT_Address addr)
{
P_W32_Address result = Zi;
if (addr.family == P_AddressFamily_Ipv4)
PLT_W32_Address result = Zi;
if (addr.family == PLT_AddressFamily_Ipv4)
{
result.family = AF_INET;
result.size = sizeof(struct sockaddr_in);
@ -88,7 +88,7 @@ P_W32_Address P_W32_Win32AddressFromPlatformAddress(P_Address addr)
}
// If supplied address has ip INADDR_ANY (0), convert ip to localhost
P_W32_Address P_W32_ConvertAnyaddrToLocalhost(P_W32_Address addr)
PLT_W32_Address PLT_W32_ConvertAnyaddrToLocalhost(PLT_W32_Address addr)
{
if (addr.family == AF_INET)
{
@ -128,19 +128,19 @@ P_W32_Address P_W32_ConvertAnyaddrToLocalhost(P_W32_Address addr)
return addr;
}
P_Address P_W32_PlatformAddressFromWin32Address(P_W32_Address ws_addr)
PLT_Address PLT_W32_PlatformAddressFromWin32Address(PLT_W32_Address ws_addr)
{
P_Address result = Zi;
PLT_Address result = Zi;
if (ws_addr.family == AF_INET)
{
result.family = P_AddressFamily_Ipv4;
result.family = PLT_AddressFamily_Ipv4;
result.portnb = ws_addr.sin.sin_port;
CopyBytes(result.ipnb, &ws_addr.sin.sin_addr, 4);
result.valid = 1;
}
else if (ws_addr.family == AF_INET6)
{
result.family = P_AddressFamily_Ipv6;
result.family = PLT_AddressFamily_Ipv6;
result.portnb = ws_addr.sin6.sin6_port;
CopyBytes(result.ipnb, &ws_addr.sin6.sin6_addr.s6_addr, 16);
result.valid = 1;
@ -151,7 +151,7 @@ P_Address P_W32_PlatformAddressFromWin32Address(P_W32_Address ws_addr)
////////////////////////////////////////////////////////////
//~ Timer job
void P_W32_SyncTimerForever(WaveLaneCtx *lane)
void PLT_W32_SyncTimerForever(WaveLaneCtx *lane)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
@ -163,10 +163,10 @@ void P_W32_SyncTimerForever(WaveLaneCtx *lane)
}
i32 periods_index = 0;
i64 periods[P_W32_NumRollingTimerPeriods] = Zi;
i64 periods[PLT_W32_NumRollingTimerPeriods] = Zi;
for (i32 i = 0; i < (i32)countof(periods); ++i)
{
periods[i] = P_W32_DefaultTimerPeriodNs;
periods[i] = PLT_W32_DefaultTimerPeriodNs;
}
i64 last_cycle_ns = 0;
@ -185,7 +185,7 @@ void P_W32_SyncTimerForever(WaveLaneCtx *lane)
WaitForSingleObject(timer, INFINITE);
}
i64 now_ns = TimeNs();
i64 period_ns = last_cycle_ns == 0 ? P_W32_DefaultTimerPeriodNs : now_ns - last_cycle_ns;
i64 period_ns = last_cycle_ns == 0 ? PLT_W32_DefaultTimerPeriodNs : now_ns - last_cycle_ns;
last_cycle_ns = now_ns;
// Compute mean period
@ -201,18 +201,18 @@ void P_W32_SyncTimerForever(WaveLaneCtx *lane)
periods_sum_ns += (f64)periods[i];
}
f64 mean_ns = periods_sum_ns / (f64)countof(periods);
Atomic64Set(&P_W32.average_timer_period_ns.v, RoundF64(mean_ns));
Atomic64Set(&PLT_W32.average_timer_period_ns.v, RoundF64(mean_ns));
}
// Update fence
SetFence(&P_W32.timer_fence, now_ns);
SetFence(&PLT_W32.timer_fence, now_ns);
}
}
////////////////////////////////////////////////////////////
//~ @hookimpl File system
String P_GetWritePath(Arena *arena)
String PLT_GetWritePath(Arena *arena)
{
u16 *p = 0;
// TODO: cache this?
@ -225,13 +225,13 @@ String P_GetWritePath(Arena *arena)
String path = Zi;
if (result == S_OK)
{
path = P_W32_StringFromWin32Path(arena, p);
path = PLT_W32_StringFromWin32Path(arena, p);
}
CoTaskMemFree(p);
return path;
}
b32 P_IsFile(String path)
b32 PLT_IsFile(String path)
{
TempArena scratch = BeginScratchNoConflict();
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
@ -240,7 +240,7 @@ b32 P_IsFile(String path)
return attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY);
}
b32 P_IsDir(String path)
b32 PLT_IsDir(String path)
{
TempArena scratch = BeginScratchNoConflict();
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
@ -249,7 +249,7 @@ b32 P_IsDir(String path)
return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY);
}
void P_MkDir(String path)
void PLT_MkDir(String path)
{
TempArena scratch = BeginScratchNoConflict();
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
@ -292,10 +292,10 @@ void P_MkDir(String path)
EndScratch(scratch);
}
P_File P_OpenFileRead(String path)
PLT_File PLT_OpenFileRead(String path)
{
TempArena scratch = BeginScratchNoConflict();
P_File file = Zi;
PLT_File file = Zi;
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
HANDLE handle = CreateFileW(
@ -314,10 +314,10 @@ P_File P_OpenFileRead(String path)
return file;
}
P_File P_OpenFileReadWait(String path)
PLT_File PLT_OpenFileReadWait(String path)
{
TempArena scratch = BeginScratchNoConflict();
P_File file = Zi;
PLT_File file = Zi;
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
i32 delay_ms = 1;
@ -344,10 +344,10 @@ P_File P_OpenFileReadWait(String path)
return file;
}
P_File P_OpenFileWrite(String path)
PLT_File PLT_OpenFileWrite(String path)
{
TempArena scratch = BeginScratchNoConflict();
P_File file = Zi;
PLT_File file = Zi;
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
HANDLE handle = CreateFileW(
@ -366,10 +366,10 @@ P_File P_OpenFileWrite(String path)
return file;
}
P_File P_OpenFileAppend(String path)
PLT_File PLT_OpenFileAppend(String path)
{
TempArena scratch = BeginScratchNoConflict();
P_File file = Zi;
PLT_File file = Zi;
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
HANDLE handle = CreateFileW(
@ -388,7 +388,7 @@ P_File P_OpenFileAppend(String path)
return file;
}
void P_CloseFile(P_File file)
void PLT_CloseFile(PLT_File file)
{
if (file.handle)
{
@ -396,7 +396,7 @@ void P_CloseFile(P_File file)
}
}
String P_ReadFile(Arena *arena, P_File file)
String PLT_ReadFile(Arena *arena, PLT_File file)
{
i64 size = 0;
GetFileSizeEx((HANDLE)file.handle, (PLARGE_INTEGER)&size);
@ -419,7 +419,7 @@ String P_ReadFile(Arena *arena, P_File file)
return result;
}
void P_WriteFile(P_File file, String data)
void PLT_WriteFile(PLT_File file, String data)
{
// TODO: Check what the real data limit is and chunk sequentially based on
// that (rather than failing)
@ -444,14 +444,14 @@ void P_WriteFile(P_File file, String data)
);
}
u64 P_GetFileSize(P_File file)
u64 PLT_GetFileSize(PLT_File file)
{
LARGE_INTEGER li_file_size;
GetFileSizeEx((HANDLE)file.handle, &li_file_size);
return (u64)(li_file_size.QuadPart > 0 ? li_file_size.QuadPart : 0);
}
P_FileTime P_GetFileTime(P_File file)
PLT_FileTime PLT_GetFileTime(PLT_File file)
{
// Get file times
FILETIME ft_created;
@ -473,27 +473,27 @@ P_FileTime P_GetFileTime(P_File file)
FileTimeToSystemTime(&ft_accessed, &st_accessed);
FileTimeToSystemTime(&ft_modified, &st_modified);
return (P_FileTime)
return (PLT_FileTime)
{
.created = P_W32_DateTimeFromWin32SystemTime(st_created),
.accessed = P_W32_DateTimeFromWin32SystemTime(st_accessed),
.modified = P_W32_DateTimeFromWin32SystemTime(st_modified)
.created = PLT_W32_DateTimeFromWin32SystemTime(st_created),
.accessed = PLT_W32_DateTimeFromWin32SystemTime(st_accessed),
.modified = PLT_W32_DateTimeFromWin32SystemTime(st_modified)
};
}
else
{
return (P_FileTime) { 0 };
return (PLT_FileTime) { 0 };
}
}
////////////////////////////////////////////////////////////
//~ @hookimpl File map
P_FileMap P_OpenFileMap(P_File file)
PLT_FileMap PLT_OpenFileMap(PLT_File file)
{
P_FileMap map = Zi;
PLT_FileMap map = Zi;
u64 size = P_GetFileSize(file);
u64 size = PLT_GetFileSize(file);
u8 *base_ptr = 0;
HANDLE map_handle = 0;
@ -536,7 +536,7 @@ P_FileMap P_OpenFileMap(P_File file)
return map;
}
void P_CloseFileMap(P_FileMap map)
void PLT_CloseFileMap(PLT_FileMap map)
{
if (map.mapped_memory.text)
{
@ -548,7 +548,7 @@ void P_CloseFileMap(P_FileMap map)
}
}
String P_GetFileMapData(P_FileMap map)
String PLT_GetFileMapData(PLT_FileMap map)
{
return map.mapped_memory;
}
@ -556,9 +556,9 @@ String P_GetFileMapData(P_FileMap map)
////////////////////////////////////////////////////////////
//~ @hookimpl Address helpers
P_Address P_AddressFromIpPortCstr(char *ip_cstr, char *port_cstr)
PLT_Address PLT_AddressFromIpPortCstr(char *ip_cstr, char *port_cstr)
{
P_Address result = Zi;
PLT_Address result = Zi;
struct addrinfo hints = Zi;
hints.ai_family = AF_UNSPEC;
@ -575,7 +575,7 @@ P_Address P_AddressFromIpPortCstr(char *ip_cstr, char *port_cstr)
{
struct sockaddr_in *sockaddr = (struct sockaddr_in *)ai_result->ai_addr;
result.valid = 1;
result.family = P_AddressFamily_Ipv4;
result.family = PLT_AddressFamily_Ipv4;
result.portnb = sockaddr->sin_port;
StaticAssert(sizeof(sockaddr->sin_addr) == 4);
CopyBytes(result.ipnb, (void *)&sockaddr->sin_addr, 4);
@ -586,7 +586,7 @@ P_Address P_AddressFromIpPortCstr(char *ip_cstr, char *port_cstr)
// TODO: Enable ipv6
// struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)ai_result->ai_addr;
// result.valid = 1;
// result.family = P_AddressFamily_Ipv6;
// result.family = PLT_AddressFamily_Ipv6;
// result.portnb = sockaddr->sin6_port;
// StaticAssert(sizeof(sockaddr->sin6_addr) == 16);
// CopyBytes(result.ipnb, (void *)&sockaddr->sin6_addr, 16);
@ -600,7 +600,7 @@ P_Address P_AddressFromIpPortCstr(char *ip_cstr, char *port_cstr)
return result;
}
P_Address P_AddressFromString(String str)
PLT_Address PLT_AddressFromString(String str)
{
// Parse string into ip & port
u8 ip_buff[1024];
@ -691,11 +691,11 @@ P_Address P_AddressFromString(String str)
}
}
P_Address result = P_AddressFromIpPortCstr(ip_cstr, port_cstr);
PLT_Address result = PLT_AddressFromIpPortCstr(ip_cstr, port_cstr);
return result;
}
P_Address P_AddressFromPort(u16 port)
PLT_Address PLT_AddressFromPort(u16 port)
{
u8 port_buff[128];
char *port_cstr = 0;
@ -721,16 +721,16 @@ P_Address P_AddressFromPort(u16 port)
}
}
P_Address result = P_AddressFromIpPortCstr(0, port_cstr);
PLT_Address result = PLT_AddressFromIpPortCstr(0, port_cstr);
return result;
}
String P_StringFromAddress(Arena *arena, P_Address address)
String PLT_StringFromAddress(Arena *arena, PLT_Address address)
{
String result = Zi;
if (address.family == P_AddressFamily_Ipv6)
if (address.family == PLT_AddressFamily_Ipv6)
{
// TODO
}
@ -748,7 +748,7 @@ String P_StringFromAddress(Arena *arena, P_Address address)
return result;
}
b32 P_MatchAddress(P_Address a, P_Address b)
b32 PLT_MatchAddress(PLT_Address a, PLT_Address b)
{
return MatchStruct(&a, &b);
}
@ -756,26 +756,26 @@ b32 P_MatchAddress(P_Address a, P_Address b)
////////////////////////////////////////////////////////////
//~ @hookimpl Socket
P_Sock *P_AcquireSock(u16 listen_port, u64 sndbuf_size, u64 rcvbuf_size)
PLT_Sock *PLT_AcquireSock(u16 listen_port, u64 sndbuf_size, u64 rcvbuf_size)
{
P_W32_Sock *ws = 0;
PLT_W32_Sock *ws = 0;
{
Lock lock = LockE(&P_W32.socks_mutex);
if (P_W32.first_free_sock)
Lock lock = LockE(&PLT_W32.socks_mutex);
if (PLT_W32.first_free_sock)
{
ws = P_W32.first_free_sock;
P_W32.first_free_sock = ws->next_free;
ws = PLT_W32.first_free_sock;
PLT_W32.first_free_sock = ws->next_free;
}
else
{
ws = PushStructNoZero(P_W32.socks_arena, P_W32_Sock);
ws = PushStructNoZero(PLT_W32.socks_arena, PLT_W32_Sock);
}
Unlock(&lock);
}
ZeroStruct(ws);
P_Address addr = P_AddressFromPort(listen_port);
P_W32_Address bind_address = P_W32_Win32AddressFromPlatformAddress(addr);
PLT_Address addr = PLT_AddressFromPort(listen_port);
PLT_W32_Address bind_address = PLT_W32_Win32AddressFromPlatformAddress(addr);
ws->sock = socket(bind_address.family, SOCK_DGRAM, IPPROTO_UDP);
{
i32 sb = sndbuf_size;
@ -787,39 +787,39 @@ P_Sock *P_AcquireSock(u16 listen_port, u64 sndbuf_size, u64 rcvbuf_size)
}
bind(ws->sock, &bind_address.sa, bind_address.size);
return (P_Sock *)ws;
return (PLT_Sock *)ws;
}
void P_ReleaseSock(P_Sock *sock)
void PLT_ReleaseSock(PLT_Sock *sock)
{
P_W32_Sock *ws = (P_W32_Sock *)sock;
PLT_W32_Sock *ws = (PLT_W32_Sock *)sock;
closesocket(ws->sock);
Lock lock = LockE(&P_W32.socks_mutex);
Lock lock = LockE(&PLT_W32.socks_mutex);
{
ws->next_free = P_W32.first_free_sock;
P_W32.first_free_sock = ws;
ws->next_free = PLT_W32.first_free_sock;
PLT_W32.first_free_sock = ws;
}
Unlock(&lock);
}
P_SockReadResult P_ReadSock(Arena *arena, P_Sock *sock)
PLT_SockReadResult PLT_ReadSock(Arena *arena, PLT_Sock *sock)
{
P_W32_Sock *ws = (P_W32_Sock *)sock;
PLT_W32_Sock *ws = (PLT_W32_Sock *)sock;
u64 read_buff_size = Kibi(64);
String read_buff = Zi;
read_buff.len = read_buff_size;
read_buff.text = PushStructsNoZero(arena, u8, read_buff_size);
P_SockReadResult result = Zi;
PLT_SockReadResult result = Zi;
P_W32_Address ws_addr = Zi;
PLT_W32_Address ws_addr = Zi;
ws_addr.size = sizeof(ws_addr.sas);
i32 size = recvfrom(ws->sock, (char *)read_buff.text, read_buff.len, 0, &ws_addr.sa, &ws_addr.size);
ws_addr.family = ws_addr.sin.sin_family;
result.address = P_W32_PlatformAddressFromWin32Address(ws_addr);
result.address = PLT_W32_PlatformAddressFromWin32Address(ws_addr);
if (size >= 0)
{
AddGstat(SockBytesReceived, size);
@ -845,10 +845,10 @@ P_SockReadResult P_ReadSock(Arena *arena, P_Sock *sock)
return result;
}
void P_WriteSock(P_Sock *sock, P_Address address, String data)
void PLT_WriteSock(PLT_Sock *sock, PLT_Address address, String data)
{
P_W32_Sock *ws = (P_W32_Sock *)sock;
P_W32_Address ws_addr = P_W32_Win32AddressFromPlatformAddress(address);
PLT_W32_Sock *ws = (PLT_W32_Sock *)sock;
PLT_W32_Address ws_addr = PLT_W32_Win32AddressFromPlatformAddress(address);
i32 size = sendto(ws->sock, (char *)data.text, data.len, 0, &ws_addr.sa, ws_addr.size);
if (size > 0)
{
@ -867,7 +867,7 @@ void P_WriteSock(P_Sock *sock, P_Address address, String data)
////////////////////////////////////////////////////////////
//~ @hookimpl Utils
void P_MessageBox(P_MessageBoxKind kind, String message)
void PLT_MessageBox(PLT_MessageBoxKind kind, String message)
{
TempArena scratch = BeginScratchNoConflict();
@ -877,24 +877,24 @@ void P_MessageBox(P_MessageBoxKind kind, String message)
switch (kind)
{
case P_MessageBoxKind_Ok:
case PLT_MessageBoxKind_Ok:
{
mbox_type |= MB_ICONINFORMATION;
} break;
case P_MessageBoxKind_Warning:
case PLT_MessageBoxKind_Warning:
{
title = L"Warning";
mbox_type |= MB_ICONWARNING;
} break;
case P_MessageBoxKind_Error:
case PLT_MessageBoxKind_Error:
{
title = L"Error";
mbox_type |= MB_ICONERROR;
} break;
case P_MessageBoxKind_Fatal:
case PLT_MessageBoxKind_Fatal:
{
title = L"Fatal error";
mbox_type |= MB_ICONSTOP;
@ -907,7 +907,7 @@ void P_MessageBox(P_MessageBoxKind kind, String message)
EndScratch(scratch);
}
void P_SetClipboardText(String str)
void PLT_SetClipboardText(String str)
{
if (OpenClipboard(0))
{
@ -929,7 +929,7 @@ void P_SetClipboardText(String str)
}
}
String P_GetClipboardText(Arena *arena)
String PLT_GetClipboardText(Arena *arena)
{
String result = Zi;
if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(0))
@ -949,28 +949,28 @@ String P_GetClipboardText(Arena *arena)
////////////////////////////////////////////////////////////
//~ @hookimpl Timer
Fence *P_GetTimerFence(void)
Fence *PLT_GetTimerFence(void)
{
return &P_W32.timer_fence;
return &PLT_W32.timer_fence;
}
i64 P_GetCurrentTimerPeriodNs(void)
i64 PLT_GetCurrentTimerPeriodNs(void)
{
return Atomic64Fetch(&P_W32.average_timer_period_ns.v);
return Atomic64Fetch(&PLT_W32.average_timer_period_ns.v);
}
////////////////////////////////////////////////////////////
//~ @hookimpl Sleep
void P_SleepPrecise(i64 sleep_time_ns)
void PLT_SleepPrecise(i64 sleep_time_ns)
{
i64 now_ns = TimeNs();
i64 target_ns = now_ns + sleep_time_ns;
// Sleep on timer to conserve power
{
Fence *timer_fence = P_GetTimerFence();
i64 timer_period_ns = P_GetCurrentTimerPeriodNs();
Fence *timer_fence = PLT_GetTimerFence();
i64 timer_period_ns = PLT_GetCurrentTimerPeriodNs();
i64 timer_tolerance_ns = timer_period_ns * 0.5;
i64 target_timer_sleep_ns = target_ns - timer_period_ns - timer_tolerance_ns;
YieldOnFence(timer_fence, target_timer_sleep_ns);
@ -985,7 +985,7 @@ void P_SleepPrecise(i64 sleep_time_ns)
}
}
void P_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns)
void PLT_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns)
{
if (last_frame_time_ns != 0 && target_dt_ns > 0)
{
@ -994,7 +994,7 @@ void P_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns)
i64 sleep_time_ns = target_dt_ns - last_frame_dt_ns;
if (sleep_time_ns > 0)
{
P_SleepPrecise(sleep_time_ns);
PLT_SleepPrecise(sleep_time_ns);
}
}
}

View File

@ -1,18 +1,18 @@
////////////////////////////////////////////////////////////
//~ Watch types
Struct(P_W32_Watch)
Struct(PLT_W32_Watch)
{
HANDLE dir_handle;
HANDLE wake_handle;
P_W32_Watch *next_free;
PLT_W32_Watch *next_free;
u8 results_buff[Kibi(64)];
};
////////////////////////////////////////////////////////////
//~ Address types
Struct(P_W32_Address)
Struct(PLT_W32_Address)
{
i32 size;
i32 family;
@ -28,56 +28,56 @@ Struct(P_W32_Address)
////////////////////////////////////////////////////////////
//~ Sock types
Struct(P_W32_Sock)
Struct(PLT_W32_Sock)
{
SOCKET sock;
P_W32_Sock *next_free;
PLT_W32_Sock *next_free;
};
////////////////////////////////////////////////////////////
//~ State types
#define P_W32_NumRollingTimerPeriods 500
#define P_W32_DefaultTimerPeriodNs 50000000
#define PLT_W32_NumRollingTimerPeriods 500
#define PLT_W32_DefaultTimerPeriodNs 50000000
Struct(P_W32_Ctx)
Struct(PLT_W32_Ctx)
{
//- Watches pool
Mutex watches_mutex;
Arena *watches_arena;
P_W32_Watch *watches_first_free;
PLT_W32_Watch *watches_first_free;
//- Socket pool
WSADATA wsa_data;
Arena *socks_arena;
Mutex socks_mutex;
P_W32_Sock *first_free_sock;
PLT_W32_Sock *first_free_sock;
//- Timer
Fence timer_fence;
Atomic64Padded average_timer_period_ns;
};
extern P_W32_Ctx P_W32;
extern PLT_W32_Ctx PLT_W32;
////////////////////////////////////////////////////////////
//~ Time
DateTime P_W32_DateTimeFromWin32SystemTime(SYSTEMTIME st);
DateTime PLT_W32_DateTimeFromWin32SystemTime(SYSTEMTIME st);
////////////////////////////////////////////////////////////
//~ File system
String P_W32_StringFromWin32Path(Arena *arena, wchar_t *src);
String PLT_W32_StringFromWin32Path(Arena *arena, wchar_t *src);
////////////////////////////////////////////////////////////
//~ Address
P_W32_Address P_W32_Win32AddressFromPlatformAddress(P_Address addr);
P_W32_Address P_W32_ConvertAnyaddrToLocalhost(P_W32_Address addr);
P_Address P_W32_PlatformAddressFromWin32Address(P_W32_Address ws_addr);
PLT_W32_Address PLT_W32_Win32AddressFromPlatformAddress(PLT_Address addr);
PLT_W32_Address PLT_W32_ConvertAnyaddrToLocalhost(PLT_W32_Address addr);
PLT_Address PLT_W32_PlatformAddressFromWin32Address(PLT_W32_Address ws_addr);
////////////////////////////////////////////////////////////
//~ Timer job
void P_W32_SyncTimerForever(WaveLaneCtx *lane);
void PLT_W32_SyncTimerForever(WaveLaneCtx *lane);

View File

@ -1,7 +1,12 @@
@Layer pp
//////////////////////////////
//- Dependencies
//- Resources
@Dep pp_sim
@Dep pp_vis
@EmbedDir P_Resources pp_res
//////////////////////////////
//- Impl
@DefaultDownstream Any pp_vis
@DefaultDownstream Any pp_sim

View File

@ -3,6 +3,7 @@
//////////////////////////////
//- Dependencies
@Dep pp
@Dep platform
//////////////////////////////

View File

@ -1676,7 +1676,7 @@ void S_TickForever(WaveLaneCtx *lane)
if (!shutdown)
{
i64 step_dt_ns = NsFromSeconds(1) / SIM_TICKS_PER_SECOND;
P_SleepFrame(frame_begin_ns, step_dt_ns);
PLT_SleepFrame(frame_begin_ns, step_dt_ns);
}
}

View File

@ -3,6 +3,7 @@
//////////////////////////////
//- Dependencies
@Dep pp
@Dep gpu
@Dep glyph_cache
@Dep platform
@ -13,8 +14,6 @@
//////////////////////////////
//- Resources
@EmbedDir V_Resources pp_vis_res
@ComputeShader V_ClearCellsCS
@ComputeShader V_ClearParticlesCS
@ComputeShader V_BackdropCS
@ -31,10 +30,8 @@
//- Api
@IncludeC pp_vis_shared.cgh
@IncludeG pp_vis_shared.cgh
@IncludeC pp_vis_core.h
@IncludeG pp_vis_shared.cgh
@IncludeG pp_vis_gpu.gh
@Bootstrap V_Bootstrap
@ -43,5 +40,4 @@
//- Impl
@IncludeC pp_vis_core.c
@IncludeG pp_vis_gpu.g

View File

@ -246,7 +246,7 @@ V_WidgetTheme V_GetWidgetTheme(void)
{
V_WidgetTheme theme = Zi;
theme.text_font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/seguisb.ttf")));
theme.text_font = GC_FontKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("font/seguisb.ttf")));
theme.icon_font = UI_BuiltinIconFont();
// theme.font_size = 14;

File diff suppressed because it is too large Load Diff

View File

@ -1,336 +0,0 @@
////////////////////////////////////////////////////////////
//~ Binds
//- Bind kinds
Enum(PP_BindKind)
{
PP_BindKind_None,
PP_BindKind_MoveUp,
PP_BindKind_MoveDown,
PP_BindKind_MoveLeft,
PP_BindKind_MoveRight,
PP_BindKind_Walk,
PP_BindKind_Fire,
PP_BindKind_AltFire,
PP_BindKind_TestTile,
PP_BindKind_DebugClear,
PP_BindKind_DebugSpawn1,
PP_BindKind_DebugSpawn2,
PP_BindKind_DebugSpawn3,
PP_BindKind_DebugSpawn4,
PP_BindKind_DebugWalls,
PP_BindKind_DebugFollow,
PP_BindKind_DebugDraw,
PP_BindKind_DebugConsole,
PP_BindKind_DebugCamera,
PP_BindKind_DebugLister,
PP_BindKind_DebugPause,
PP_BindKind_DebugStep,
PP_BindKind_DebugDrag,
PP_BindKind_DebugDelete,
PP_BindKind_DebugTeleport,
PP_BindKind_DebugExplode,
PP_BindKind_DebugToggleTopmost,
PP_BindKind_DebugUi,
PP_BindKind_FullscreenMod,
PP_BindKind_Fullscreen,
PP_BindKind_ZoomIn,
PP_BindKind_ZoomOut,
PP_BindKind_Pan,
#if IsRtcEnabled
/* Debug */
PP_BindKind_ResetDebugSteps,
PP_BindKind_IncrementDebugSteps,
PP_BindKind_DecrementDebugSteps,
#endif
PP_BindKind_COUNT
};
//- Test bindings
/* TODO: Remove this */
Global Readonly PP_BindKind g_binds[Btn_COUNT] = {
[Btn_W] = PP_BindKind_MoveUp,
[Btn_S] = PP_BindKind_MoveDown,
[Btn_A] = PP_BindKind_MoveLeft,
[Btn_D] = PP_BindKind_MoveRight,
[Btn_M1] = PP_BindKind_Fire,
[Btn_M2] = PP_BindKind_AltFire,
#if 0
[Btn_Alt] = PP_BindKind_Walk,
#endif
/* Testing */
[Btn_Z] = PP_BindKind_TestTile,
[Btn_M5] = PP_BindKind_DebugDrag,
[Btn_M4] = PP_BindKind_DebugDelete,
[Btn_F] = PP_BindKind_DebugExplode,
[Btn_T] = PP_BindKind_DebugTeleport,
[Btn_C] = PP_BindKind_DebugClear,
[Btn_1] = PP_BindKind_DebugSpawn1,
[Btn_2] = PP_BindKind_DebugSpawn2,
[Btn_3] = PP_BindKind_DebugSpawn3,
[Btn_4] = PP_BindKind_DebugSpawn4,
[Btn_G] = PP_BindKind_DebugWalls,
[Btn_N] = PP_BindKind_DebugStep,
[Btn_Q] = PP_BindKind_DebugFollow,
[Btn_F1] = PP_BindKind_DebugPause,
[Btn_F2] = PP_BindKind_DebugCamera,
[Btn_F3] = PP_BindKind_DebugDraw,
[Btn_Tab] = PP_BindKind_DebugLister,
[Btn_F4] = PP_BindKind_DebugToggleTopmost,
[Btn_F5] = PP_BindKind_DebugUi,
[Btn_GraveAccent] = PP_BindKind_DebugConsole,
[Btn_Alt] = PP_BindKind_FullscreenMod,
[Btn_Enter] = PP_BindKind_Fullscreen,
[Btn_MWheelUp] = PP_BindKind_ZoomIn,
[Btn_MWheelDown] = PP_BindKind_ZoomOut,
[Btn_M3] = PP_BindKind_Pan,
#if IsRtcEnabled
[Btn_ForwardSlash] = PP_BindKind_ResetDebugSteps,
[Btn_Comma] = PP_BindKind_DecrementDebugSteps,
[Btn_Period] = PP_BindKind_IncrementDebugSteps
#endif
};
////////////////////////////////////////////////////////////
//~ Stats
Struct(PP_SecondsStat)
{
u64 last_second_start;
u64 last_second_end;
u64 last_second;
};
////////////////////////////////////////////////////////////
//~ Console log
Struct(PP_ConsoleLog)
{
String msg;
i32 level;
i32 color_index;
DateTime datetime;
PP_ConsoleLog *prev;
PP_ConsoleLog *next;
};
////////////////////////////////////////////////////////////
//~ Sim decode queue
Struct(PP_DecodeQueueNode)
{
PP_Client *client;
u64 tick;
u64 base_tick;
String tmp_encoded;
PP_DecodeQueueNode *next;
};
Struct(PP_DecodeQueue)
{
PP_DecodeQueueNode *first;
PP_DecodeQueueNode *last;
};
////////////////////////////////////////////////////////////
//~ State types
Struct(PP_BindState)
{
b32 is_held; /* Is this bind held down this frame */
u32 num_presses; /* How many times was this bind's pressed since last frame */
u32 num_repeats; /* How many times was this bind's key repeated since last frame */
u32 num_presses_and_repeats; /* Same as `num_presses` but includes key repeats as well */
u32 num_releases; /* How many times was this bind released since last frame */
};
Struct(PP_SharedUserState)
{
Atomic32 shutdown;
Fence shutdown_jobs_fence;
u64 shutdown_jobs_count;
Arena *arena;
String connect_address_str;
PP_ClientStore *user_client_store;
PP_Client *user_unblended_client; /* Contains snapshots received from local sim */
PP_Client *user_blended_client; /* Contains single snapshot from result of blending local sim snapshots */
PP_Snapshot *ss_blended; /* Points to blended snapshot contained in blended client */
u64 user_tick;
u64 window_os_gen;
//- Usage stats
i64 last_second_reset_ns;
PP_SecondsStat net_bytes_read;
PP_SecondsStat net_bytes_sent;
//- Renderer gbuffers
GPU_Resource *albedo;
GPU_Resource *emittance;
GPU_Resource *emittance_flood_read;
GPU_Resource *emittance_flood_target;
GPU_Resource *shade_read;
GPU_Resource *shade_target;
//- Renderer transient buffers
GPU_TransientBuffer material_instances_tbuff;
GPU_TransientBuffer grids_tbuff;
Arena *material_instances_arena;
Arena *grids_arena;
//- Renderer state
RandState frame_rand;
u64 frame_index;
i64 gpu_submit_fence_target;
//- Bind state
PP_BindState bind_states[PP_BindKind_COUNT];
//- Window -> user
Mutex sys_window_events_mutex;
Arena *sys_window_events_arena;
//- User -> sim
Mutex user_sim_cmd_mutex;
PP_ControlData user_sim_cmd_control;
PP_EntKey user_hovered_ent;
u64 last_user_sim_cmd_gen;
u64 user_sim_cmd_gen;
Atomic32 user_paused;
Atomic32 user_paused_steps;
//- Sim -> user
Mutex local_to_user_client_mutex;
PP_ClientStore *local_to_user_client_store;
PP_Client *local_to_user_client;
i64 local_to_user_client_publish_dt_ns;
i64 local_to_user_client_publish_time_ns;
//- Local sim -> user rolling window of publish time deltas
i64 last_local_to_user_snapshot_published_at_ns;
i64 average_local_to_user_snapshot_publish_dt_ns;
i64 local_sim_predicted_time_ns; /* Calculated from <last local sim to user pubilsh time> + <time since last local sim to user publish> */
i64 render_time_target_ns; /* Claculated from <local_sim_rpedicted_time_ns> - <render interp delay> */
i64 render_time_ns; /* Incremented at a constant rate based on average local to user publish delta, but snaps to render_time_target_ns if it gets too distant */
u64 local_sim_last_known_tick;
i64 local_sim_last_known_time_ns;
i64 real_dt_ns;
i64 real_time_ns;
//- Window
String window_restore;
//////////////////////////////
//- Persist start
StructRegion(AUTO_PERSIST_START);
//- Debug ui
b32 ui_debug;
PP_EntKey debug_following;
Vec2 debug_camera_pan_start;
b32 debug_camera_panning;
b32 debug_camera;
b32 lister_active;
Vec2 lister_pos;
b32 debug_draw;
b32 debug_console;
//- Per frame
Vec2I32 screen_size;
Vec2 screen_cursor;
Xform ui_to_screen_xf;
Vec2I32 ui_size;
Vec2 ui_cursor;
Xform render_to_ui_xf;
Vec2I32 render_size;
Xform world_to_render_xf;
Xform world_to_ui_xf;
Vec2 world_cursor;
Vec2 focus_send;
StructRegion(AUTO_PERSIST_END);
//- Persist end
//////////////////////////////
} extern PP_shared_user_state;
////////////////////////////////////////////////////////////
//~ Swap types
Struct(PP_SwappedUserState)
{
PP_SharedUserState s;
};
////////////////////////////////////////////////////////////
//~ Startup
void PP_StartupUser(void);
////////////////////////////////////////////////////////////
//~ Shutdown
ExitFuncDef(PP_ShutdownUser);
////////////////////////////////////////////////////////////
//~ Ui helpers
void PP_PushGameUiStyle(void);
void PP_DrawDebugXform(Xform xf, u32 color_x, u32 color_y);
void PP_DrawDebugMovement(PP_Ent *ent);
String PP_DebugStringFromEnt(Arena *arena, PP_Ent *ent);
////////////////////////////////////////////////////////////
//~ Gpu buffer helpers
GPU_Resource *PP_AcquireGbuffer(GPU_Format format, Vec2I32 size);
////////////////////////////////////////////////////////////
//~ Ent sorting
MergesortCompareFuncDef(PP_EntSortCmp, arg_a, arg_b, _);
////////////////////////////////////////////////////////////
//~ User update
void PP_UpdateUser(void);
////////////////////////////////////////////////////////////
//~ User update job
JobDecl(PP_UpdateUserOrSleep, EmptySig);
////////////////////////////////////////////////////////////
//~ User input cmds
void PP_GenerateuserInputCmds(PP_Client *user_input_client, u64 tick);
////////////////////////////////////////////////////////////
//~ Sim update job
JobDecl(PP_UpdateSim, EmptySig);

View File

@ -1,54 +0,0 @@
@Layer pp_old
//////////////////////////////
//- Dependencies
@Dep gpu
@Dep sprite
@Dep font
@Dep collider
@Dep net
@Dep mixer
@Dep rendertest
@Dep playback
@Dep platform
@Dep window
@Dep ui
//////////////////////////////
//- Resources
@EmbedDir PP_Resources pp_res
@VertexShader PP_MaterialVS
@PixelShader PP_MaterialPS
@ComputeShader PP_FloodCS
@ComputeShader PP_ShadeCS
//////////////////////////////
//- Api
@IncludeC pp_sim.h
@IncludeC pp_phys.h
@IncludeC pp_space.h
@IncludeC pp_ent.h
@IncludeC pp_step.h
@IncludeC pp_widgets.h
@IncludeC pp_draw.cgh
@IncludeG pp_draw.cgh
@IncludeC pp.h
@Bootstrap PP_StartupSim
@Bootstrap PP_StartupUser
//////////////////////////////
//- Impl
@IncludeC pp_sim.c
@IncludeC pp_phys.c
@IncludeC pp_space.c
@IncludeC pp_ent.c
@IncludeC pp_step.c
@IncludeC pp_widgets.c
@IncludeC pp.c
@IncludeG pp_draw.g

View File

@ -1,105 +0,0 @@
////////////////////////////////////////////////////////////
//~ Material types
Struct(PP_MaterialSig)
{
/* ----------------------------------------------------- */
Mat4x4 projection; /* 16 consts */
/* ----------------------------------------------------- */
SamplerStateRid sampler; /* 01 consts */
StructuredBufferRid instances; /* 01 consts */
StructuredBufferRid grids; /* 01 consts */
u32 _pad0; /* 01 consts (padding) */
/* ----------------------------------------------------- */
};
AssertRootConst(PP_MaterialSig, 20);
Struct(PP_MaterialInstance)
{
Texture2DRid tex;
u32 grid_id;
Xform xf;
Vec2 tex_uv0;
Vec2 tex_uv1;
u32 tint_srgb;
u32 is_light;
Vec3 light_emittance_srgb;
};
#define PP_DefaultMaterialInstance (PP_MaterialInstance) { \
.tex = { U32Max }, \
.grid_id = U32Max, \
.xf = XformIdentity, \
.tex_uv1 = VEC2(1, 1), \
.tint_srgb = Color_White, \
}
Struct(PP_MaterialGrid)
{
f32 line_thickness;
f32 line_spacing;
Vec2 offset;
u32 bg0_srgb;
u32 bg1_srgb;
u32 line_srgb;
u32 x_srgb;
u32 y_srgb;
};
#define PP_DefaultMaterialGrid (PP_MaterialGrid) { \
.line_thickness = 1, \
.line_spacing = 1, \
.bg0_srgb = Color_Black, \
.bg0_srgb = Color_White \
}
////////////////////////////////////////////////////////////
//~ Flood types
Struct(PP_FloodSig)
{
/* ----------------------------------------------------- */
i32 step_len; /* 01 consts */
Texture2DRid emittance; /* 01 consts */
RWTexture2DRid read; /* 01 consts */
RWTexture2DRid target; /* 01 consts */
/* ----------------------------------------------------- */
u32 tex_width; /* 01 consts */
u32 tex_height; /* 01 consts */
u32 _pad0; /* 01 consts (padding) */
u32 _pad1; /* 01 consts (padding) */
/* ----------------------------------------------------- */
};
AssertRootConst(PP_FloodSig, 8);
////////////////////////////////////////////////////////////
//~ Shade types
#define PP_ShadeFlag_None (0 << 0)
#define PP_ShadeFlag_DisableEffects (1 << 0)
#define PP_ShadeFlag_ToneMap (1 << 1)
Struct(PP_ShadeSig)
{
/* ----------------------------------------------------- */
Vec4U32 frame_seed; /* 04 consts */
/* ----------------------------------------------------- */
u32 flags; /* 01 consts */
u32 tex_width; /* 01 consts */
u32 tex_height; /* 01 consts */
f32 exposure; /* 01 consts */
/* ----------------------------------------------------- */
Vec2 camera_offset; /* 02 consts */
u32 frame_index; /* 01 consts */
Texture2DRid albedo; /* 01 consts */
/* ----------------------------------------------------- */
Texture2DRid emittance; /* 01 consts */
Texture2DRid emittance_flood; /* 01 consts */
Texture2DRid read; /* 01 consts */
RWTexture2DRid target; /* 01 consts */
/* ----------------------------------------------------- */
Texture3DRid noise; /* 01 consts */
u32 noise_tex_width; /* 01 consts */
u32 noise_tex_height; /* 01 consts */
u32 noise_tex_depth; /* 01 consts */
/* ----------------------------------------------------- */
};
AssertRootConst(PP_ShadeSig, 20);

View File

@ -1,304 +0,0 @@
ConstantBuffer<PP_MaterialSig> PP_mat_sig : register (b0);
ConstantBuffer<PP_FloodSig> PP_flood_sig : register (b0);
ConstantBuffer<PP_ShadeSig> PP_shade_sig : register (b0);
////////////////////////////////////////////////////////////
//~ Material
Struct(PP_MaterialPS_Input)
{
Semantic(Vec4, sv_position);
Semantic(nointerpolation Texture2DRid, tex);
Semantic(nointerpolation u32, grid_id);
Semantic(Vec2, tex_uv);
Semantic(Vec4, tint_lin);
Semantic(Vec4, emittance_lin);
};
Struct(PP_MaterialPS_Output)
{
Semantic(Vec4, sv_target0); /* Albedo */
Semantic(Vec4, sv_target1); /* Emittance */
};
//- Vertex shader
PP_MaterialPS_Input VSDef(PP_MaterialVS, Semantic(u32, sv_instanceid), Semantic(u32, sv_vertexid))
{
ConstantBuffer<PP_MaterialSig> sig = PP_mat_sig;
StructuredBuffer<PP_MaterialInstance> instances = UniformResourceFromRid(sig.instances);
PP_MaterialInstance instance = instances[sv_instanceid];
Vec2 mat_uv = RectUvFromVertexId(sv_vertexid);
Vec2 mat_uv_centered = mat_uv - 0.5f;
Vec2 world_pos = mul(instance.xf, Vec3(mat_uv_centered, 1)).xy;
PP_MaterialPS_Input result;
result.sv_position = mul(sig.projection, Vec4(world_pos, 0, 1));
result.tex = instance.tex;
result.grid_id = instance.grid_id;
result.tex_uv = lerp(instance.tex_uv0, instance.tex_uv1, mat_uv);
result.tint_lin = LinearFromSrgbU32(instance.tint_srgb);
result.emittance_lin = LinearFromSrgbVec4(Vec4(instance.light_emittance_srgb, instance.is_light));
return result;
}
//- Pixel shader
PP_MaterialPS_Output PSDef(PP_MaterialPS, PP_MaterialPS_Input input)
{
ConstantBuffer<PP_MaterialSig> sig = PP_mat_sig;
PP_MaterialPS_Output result;
Vec4 albedo = input.tint_lin;
/* Texture */
if (input.tex < 0xFFFFFFFF)
{
SamplerState sampler = UniformSamplerFromRid(sig.sampler);
Texture2D<Vec4> tex = NonUniformResourceFromRid(input.tex);
albedo *= tex.Sample(sampler, input.tex_uv);
}
/* Grid */
if (input.grid_id < 0xFFFFFFFF)
{
// StructuredBuffer<PP_MaterialGrid> grids = UniformResourceFromRid(sig.grids);
StructuredBuffer<PP_MaterialGrid> grids = ResourceDescriptorHeap[sig.grids];
PP_MaterialGrid grid = grids[input.grid_id];
Vec2 grid_pos = input.sv_position.xy + grid.offset;
f32 half_thickness = grid.line_thickness / 2;
f32 spacing = grid.line_spacing;
u32 color_srgb = grid.bg0_srgb;
Vec2 v = abs(round(grid_pos / spacing) * spacing - grid_pos);
f32 dist = min(v.x, v.y);
if (grid_pos.y <= half_thickness && grid_pos.y >= -half_thickness)
{
color_srgb = grid.x_srgb;
}
else if (grid_pos.x <= half_thickness && grid_pos.x >= -half_thickness)
{
color_srgb = grid.y_srgb;
}
else if (dist < half_thickness)
{
color_srgb = grid.line_srgb;
}
else
{
bool checker = 0;
u32 cell_x = (u32)(abs(grid_pos.x) / spacing) + (grid_pos.x < 0);
u32 cell_y = (u32)(abs(grid_pos.y) / spacing) + (grid_pos.y < 0);
if (cell_x % 2 == 0)
{
checker = cell_y % 2 == 0;
}
else
{
checker = cell_y % 2 == 1;
}
if (checker)
{
color_srgb = grid.bg1_srgb;
}
}
albedo = LinearFromSrgbU32(color_srgb);
}
Vec4 emittance = input.emittance_lin * albedo.a;
result.sv_target0 = albedo;
result.sv_target1 = emittance;
return result;
}
////////////////////////////////////////////////////////////
//~ Flood
//- Compute shader
[numthreads(8, 8, 1)]
void CSDef(PP_FloodCS, Semantic(Vec3U32, sv_dispatchthreadid))
{
ConstantBuffer<PP_FloodSig> sig = PP_flood_sig;
Vec2U32 id = sv_dispatchthreadid.xy;
Vec2U32 tex_size = Vec2U32(sig.tex_width, sig.tex_height);
if (id.x < tex_size.x && id.y < tex_size.y)
{
Texture2D<Vec4> emittance_tex = UniformResourceFromRid(sig.emittance);
RWTexture2D<Vec2U32> read_flood_tex = UniformResourceFromRid(sig.read);
RWTexture2D<Vec2U32> target_flood_tex = UniformResourceFromRid(sig.target);
i32 step_len = sig.step_len;
if (step_len == -1)
{
/* Seed */
Vec4 emittance = emittance_tex[id];
Vec2U32 seed = Vec2U32(0xFFFF, 0xFFFF);
if (emittance.a > 0)
{
seed = id;
}
target_flood_tex[id] = seed;
}
else
{
/* Flood */
Vec2I32 read_coords[9] = {
(Vec2I32)id + Vec2I32(-step_len, -step_len), /* top left */
(Vec2I32)id + Vec2I32(0, -step_len), /* top center */
(Vec2I32)id + Vec2I32(+step_len, -step_len), /* top right */
(Vec2I32)id + Vec2I32(-step_len, 0), /* center left */
(Vec2I32)id + Vec2I32(0, 0), /* center center */
(Vec2I32)id + Vec2I32(+step_len, 0), /* center right */
(Vec2I32)id + Vec2I32(-step_len, +step_len), /* bottom left */
(Vec2I32)id + Vec2I32(0, +step_len), /* bottom center */
(Vec2I32)id + Vec2I32(+step_len, +step_len), /* bottom right */
};
Vec2U32 closest_seed = Vec2U32(0xFFFF, 0xFFFF);
u32 closest_seed_len_sq = 0xFFFFFFFF;
for (i32 i = 0; i < 9; ++i)
{
Vec2I32 coord = read_coords[i];
if (coord.x >= 0 && coord.x < (i32)tex_size.x && coord.y >= 0 && coord.y < (i32)tex_size.y)
{
Vec2U32 seed = read_flood_tex[coord];
Vec2I32 dist_vec = (Vec2I32)id - (Vec2I32)seed;
u32 dist_len_sq = dot(dist_vec, dist_vec);
if (dist_len_sq < closest_seed_len_sq)
{
closest_seed = seed;
closest_seed_len_sq = dist_len_sq;
}
}
}
target_flood_tex[id] = closest_seed;
}
}
}
////////////////////////////////////////////////////////////
//~ Shade
#define PP_LightSamples 16
#define PP_LightMarches 16
#define PP_LightEdgeFalloff 100
//- Lighting
f32 PP_RandAngleFromPos(Vec2U32 pos, u32 ray_index)
{
ConstantBuffer<PP_ShadeSig> sig = PP_shade_sig;
Texture3D<u32> noise_tex = UniformResourceFromRid(sig.noise);
Vec3I32 noise_coord = Vec3U32(1, 1, 1);
noise_coord += Vec3I32(pos.xy, ray_index);
// noise_coord.xyz += sig.frame_seed.xyz;
noise_coord.xy -= sig.camera_offset;
u32 noise = noise_tex[noise_coord % Vec3U32(sig.noise_tex_width, sig.noise_tex_height, sig.noise_tex_depth)];
return ((f32)noise / (f32)0xFFFF) * Tau;
}
Vec3 PP_LightFromDir(Vec2U32 ray_start, Vec2 ray_dir)
{
ConstantBuffer<PP_ShadeSig> sig = PP_shade_sig;
Texture2D<Vec2U32> flood_tex = UniformResourceFromRid(sig.emittance_flood);
Texture2D<Vec4> emittance_tex = UniformResourceFromRid(sig.emittance);
Vec3 result = Vec3(0, 0, 0);
Vec2 at_f32 = ray_start;
Vec2U32 at_u32 = ray_start;
for (u32 i = 0; i < PP_LightMarches; ++i)
{
Vec2U32 flood = flood_tex[at_u32];
Vec2 dist_vec = at_f32 - (Vec2)flood;
f32 dist = length(dist_vec);
if (dist < 1)
{
/* Scale light by distance from edge so that offscreen-lights fade in/out rather than popping in */
f32 dist_x = min(abs(sig.tex_width - at_f32.x), at_f32.x);
f32 dist_y = min(abs(sig.tex_height - at_f32.y), at_f32.y);
f32 dist_scale = min(min(dist_x, dist_y) / PP_LightEdgeFalloff, 1);
result = emittance_tex[flood].rgb * dist_scale;
break;
}
else
{
at_f32 += ray_dir * dist;
at_u32 = round(at_f32);
if (at_u32.x < 0 || at_u32.x >= sig.tex_width || at_u32.y < 0 || at_u32.y >= sig.tex_height)
{
/* Ray hit edge of screen */
break;
}
}
}
return result;
}
Vec3 PP_AccumulatedLightFromPos(Vec2U32 pos)
{
Vec3 result = 0;
for (u32 i = 0; i < PP_LightSamples; ++i)
{
f32 angle = PP_RandAngleFromPos(pos, i);
Vec2 dir = Vec2(cos(angle), sin(angle));
Vec3 light_in_dir = PP_LightFromDir(pos, dir);
result += light_in_dir;
}
result /= PP_LightSamples;
return result;
}
//- Tone mapping
// ACES approximation by Krzysztof Narkowicz
// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
Vec3 PP_ToneMap(Vec3 v)
{
return saturate((v * (2.51f * v + 0.03f)) / (v * (2.43f * v + 0.59f) + 0.14f));
}
//- Compute shader
[numthreads(8, 8, 1)]
void CSDef(PP_ShadeCS, Semantic(Vec3U32, sv_dispatchthreadid))
{
ConstantBuffer<PP_ShadeSig> sig = PP_shade_sig;
Vec2U32 id = sv_dispatchthreadid.xy;
if (id.x < sig.tex_width && id.y < sig.tex_height)
{
Texture2D<Vec4> albedo_tex = UniformResourceFromRid(sig.albedo);
Texture2D<Vec4> read_tex = UniformResourceFromRid(sig.read);
RWTexture2D<Vec4> target_tex = UniformResourceFromRid(sig.target);
Vec4 color = Vec4(1, 1, 1, 1);
/* Apply albedo */
color *= albedo_tex[id];
/* Apply lighting */
if (!(sig.flags & PP_ShadeFlag_DisableEffects))
{
color.rgb *= PP_AccumulatedLightFromPos(id);
}
/* Apply tone mapping */
if (sig.flags & PP_ShadeFlag_ToneMap)
{
/* TODO: Dynamic exposure based on average scene luminance */
color.rgb *= sig.exposure;
color.rgb = PP_ToneMap(color.rgb);
}
/* Apply temporal accumulation */
f32 hysterisis = 0;
// hysterisis = 0.2;
// hysterisis = 0.4;
// hysterisis = 0.5;
// hysterisis = 0.95;
color.rgb = lerp(color.rgb, read_tex[id].rgb, hysterisis);
target_tex[id] = color;
}
}

View File

@ -1,795 +0,0 @@
////////////////////////////////////////////////////////////
//~ Acquire
PP_Ent *PP_AcquireRawEnt(PP_Snapshot *ss, PP_Ent *parent, PP_EntKey key)
{
Assert(parent->valid);
Assert(ss->valid);
Assert(ss == parent->ss);
PP_Ent *ent;
if (ss->first_free_ent > 0 && ss->first_free_ent < ss->num_ents_reserved)
{
/* Reuse from free list */
ent = &ss->ents[ss->first_free_ent];
ss->first_free_ent = ent->next_free;
}
else
{
/* Make new */
ent = PushStructNoZero(ss->ents_arena, PP_Ent);
++ss->num_ents_reserved;
}
*ent = *PP_NilEnt();
ent->ss = ss;
ent->valid = 1;
ent->owner = ss->client->player_id;
ent->_is_xform_dirty = 1;
++ss->num_ents_allocated;
PP_SetEntKey(ent, key);
PP_Link(ent, parent);
return ent;
}
/* Acquires a new entity that will not sync */
PP_Ent *PP_AcquireLocalEnt(PP_Ent *parent)
{
PP_Snapshot *ss = parent->ss;
PP_Ent *e = PP_AcquireRawEnt(ss, parent, PP_RandomKey());
e->owner = ss->local_player;
return e;
}
PP_Ent *PP_AcquireLocalEntWithKey(PP_Ent *parent, PP_EntKey key)
{
PP_Snapshot *ss = parent->ss;
PP_Ent *e = PP_AcquireRawEnt(ss, parent, key);
e->owner = ss->local_player;
return e;
}
/* Acquires a new entity to be synced to clients */
PP_Ent *PP_AcquireSyncSrcEnt(PP_Ent *parent)
{
PP_Snapshot *ss = parent->ss;
PP_Ent *e = PP_AcquireRawEnt(ss, parent, PP_RandomKey());
PP_EnableProp(e, PP_Prop_SyncSrc);
e->owner = ss->local_player;
return e;
}
PP_Ent *PP_AcquireSyncSrcEntWithKey(PP_Ent *parent, PP_EntKey key)
{
PP_Snapshot *ss = parent->ss;
PP_Ent *e = PP_AcquireRawEnt(ss, parent, key);
PP_EnableProp(e, PP_Prop_SyncSrc);
e->owner = ss->local_player;
return e;
}
/* Acquires a new entity that will sync with incoming net src ents containing key, and coming from the specified owner */
PP_Ent *PP_AcquireSyncDstEnt(PP_Ent *parent, PP_EntKey ent_id, PP_EntKey owner_id)
{
PP_Snapshot *ss = parent->ss;
PP_Ent *e = PP_AcquireRawEnt(ss, parent, ent_id);
PP_EnableProp(e, PP_Prop_SyncDst);
e->owner = owner_id;
return e;
}
////////////////////////////////////////////////////////////
//~ Release
void PP_ReleaseRawEnt(PP_Ent *ent)
{
PP_Snapshot *ss = ent->ss;
/* Release children */
PP_Ent *child = PP_EntFromKey(ss, ent->first);
while (child->valid)
{
PP_Ent *next = PP_EntFromKey(ss, child->next);
PP_ReleaseRawEnt(child);
child = next;
}
/* Release uid */
PP_SetEntKey(ent, PP_NilEntKey);
/* Release */
ent->valid = 0;
ent->next_free = ss->first_free_ent;
ss->first_free_ent = PP_IndexFromEnt(ss, ent);
--ss->num_ents_allocated;
}
void PP_Release(PP_Ent *ent)
{
PP_Snapshot *ss = ent->ss;
PP_Ent *parent = PP_EntFromKey(ss, ent->parent);
if (parent->valid)
{
PP_Unlink(ent);
}
PP_ReleaseRawEnt(ent);
}
void PP_ReleaseAllWithProp(PP_Snapshot *ss, PP_Prop prop)
{
TempArena scratch = BeginScratchNoConflict();
PP_Ent **ents_to_release = PushDry(scratch.arena, PP_Ent *);
u64 ents_to_release_count = 0;
for (u64 ent_index = 0; ent_index < ss->num_ents_reserved; ++ent_index)
{
PP_Ent *ent = &ss->ents[ent_index];
if (ent->valid && PP_HasProp(ent, prop))
{
*PushStructNoZero(scratch.arena, PP_Ent *) = ent;
++ents_to_release_count;
}
}
// Release from snapshot
//
// TODO: Breadth first iteration to only release parent entities (since
// child entities will be released along with parent anyway)
for (u64 i = 0; i < ents_to_release_count; ++i)
{
PP_Ent *ent = ents_to_release[i];
if (ent->valid && !ent->is_root && !PP_HasProp(ent, PP_Prop_Cmd) && !PP_HasProp(ent, PP_Prop_Player))
{
PP_Release(ent);
}
}
EndScratch(scratch);
}
////////////////////////////////////////////////////////////
//~ Activate
void PP_ActivateEnt(PP_Ent *ent, u64 current_tick)
{
PP_EnableProp(ent, PP_Prop_Active);
ent->activation_tick = current_tick;
++ent->continuity_gen;
}
////////////////////////////////////////////////////////////
//~ Ent key
u32 PP_IndexFromEnt(PP_Snapshot *ss, PP_Ent *ent)
{
return ent - ss->ents;
}
PP_Ent *PP_EntFromIndex(PP_Snapshot *ss, u32 index)
{
if (index > 0 && index < ss->num_ents_reserved)
{
return &ss->ents[index];
}
else
{
return PP_NilEnt();
}
}
PP_EntBin *PP_EntBinFromKey(PP_Snapshot *ss, PP_EntKey key)
{
return &ss->key_bins[key.uid.lo % ss->num_key_bins];
}
/* NOTE: This should only really happen during ent allocation (it doesn't make sense for an allocated ent's key to change) */
void PP_SetEntKey(PP_Ent *ent, PP_EntKey key)
{
PP_Snapshot *ss = ent->ss;
PP_EntKey old_id = ent->key;
if (!PP_EqEntKey(old_id, key))
{
/* Release old from lookup */
if (!PP_IsNilEntKey(old_id))
{
PP_EntBin *bin = PP_EntBinFromKey(ss, old_id);
u32 prev_index = 0;
u32 next_index = 0;
u32 search_index = bin->first;
PP_Ent *prev = PP_NilEnt();
PP_Ent *next = PP_NilEnt();
PP_Ent *search = PP_EntFromIndex(ss, search_index);
while (search->valid)
{
next_index = search->next_in_id_bin;
next = PP_EntFromIndex(ss, next_index);
if (PP_EqEntKey(search->key, old_id))
{
break;
}
prev_index = search_index;
prev = search;
search_index = next_index;
search = next;
}
/* Old key not in bin, this should be impossible. */
Assert(search->valid);
if (prev->valid)
{
prev->next_in_id_bin = next_index;
}
else
{
bin->first = next_index;
}
if (next->valid)
{
next->prev_in_id_bin = prev_index;
}
else
{
bin->last = prev_index;
}
}
/* Insert new key into lookup */
if (!PP_IsNilEntKey(key))
{
if (IsRtcEnabled)
{
PP_Ent *existing = PP_EntFromKey(ss, key);
/* Collision should be extremely unlikely under normal circumstances, there's probably a logic error somewhere. */
Assert(!existing->valid);
}
PP_EntBin *bin = PP_EntBinFromKey(ss, key);
u32 ent_index = PP_IndexFromEnt(ss, ent);
PP_Ent *last = PP_EntFromIndex(ss, bin->last);
if (last->valid)
{
last->next_in_id_bin = ent_index;
ent->prev_in_id_bin = bin->last;
}
else
{
bin->first = ent_index;
ent->prev_in_id_bin = 0;
}
bin->last = ent_index;
}
ent->key = key;
}
}
PP_Ent *PP_EntFromKey(PP_Snapshot *ss, PP_EntKey key)
{
PP_Ent *result = PP_NilEnt();
if (!PP_IsNilEntKey(key) && ss->valid)
{
PP_EntBin *bin = PP_EntBinFromKey(ss, key);
for (PP_Ent *e = PP_EntFromIndex(ss, bin->first); e->valid; e = PP_EntFromIndex(ss, e->next_in_id_bin))
{
if (PP_EqEntKey(e->key, key))
{
result = e;
break;
}
}
}
return result;
}
PP_EntKey PP_RandomKey(void)
{
PP_EntKey result = ZI;
result.uid = UidFromTrueRand();
return result;
}
/* Returns the deterministic key of the contact constraint ent key that should be produced from e0 & e1 colliding */
PP_EntKey PP_ContactConstraintKeyFromContactingKeys(PP_EntKey player_id, PP_EntKey id0, PP_EntKey id1)
{
PP_EntKey result = ZI;
result.uid = PP_ContactBasisUid;
result.uid = CombineUid(result.uid, player_id.uid);
result.uid = CombineUid(result.uid, id0.uid);
result.uid = CombineUid(result.uid, id1.uid);
return result;
}
/* Returns the deterministic key of the debug contact constraint ent key that should be produced from e0 & e1 colliding */
PP_EntKey PP_CollisionDebugKeyFromKeys(PP_EntKey player_id, PP_EntKey id0, PP_EntKey id1)
{
PP_EntKey result = ZI;
result.uid = PP_CollisionDebugBasisUid;
result.uid = CombineUid(result.uid, player_id.uid);
result.uid = CombineUid(result.uid, id0.uid);
result.uid = CombineUid(result.uid, id1.uid);
return result;
}
/* Returns the deterministic key of the tile chunk that should be produced at chunk pos */
PP_EntKey PP_TileChunkKeyFromIndex(Vec2I32 chunk_index)
{
PP_EntKey result = ZI;
result.uid = PP_TileChunkBasisUid;
result.uid = CombineUid(result.uid, UID(RandU64FromSeed(chunk_index.x), RandU64FromSeed(chunk_index.y)));
return result;
}
////////////////////////////////////////////////////////////
//~ Query
PP_Ent *PP_FirstWithProp(PP_Snapshot *ss, PP_Prop prop)
{
u64 count = ss->num_ents_reserved;
PP_Ent *entities = ss->ents;
for (u64 ent_index = 0; ent_index < count; ++ent_index)
{
PP_Ent *ent = &entities[ent_index];
if (ent->valid && PP_HasProp(ent, prop))
{
return ent;
}
}
return PP_NilEnt();
}
PP_Ent *PP_FirstWithAllProps(PP_Snapshot *ss, PP_PropArray props)
{
u64 count = ss->num_ents_reserved;
PP_Ent *entities = ss->ents;
for (u64 ent_index = 0; ent_index < count; ++ent_index)
{
PP_Ent *ent = &entities[ent_index];
if (ent->valid)
{
b32 all = 1;
for (u64 i = 0; i < props.count; ++i)
{
if (!PP_HasProp(ent, props.props[i]))
{
all = 0;
break;
}
}
if (all)
{
return ent;
}
}
}
return PP_NilEnt();
}
////////////////////////////////////////////////////////////
//~ Tree
void PP_Link(PP_Ent *ent, PP_Ent *parent)
{
PP_Snapshot *ss = ent->ss;
PP_Ent *old_parent = PP_EntFromKey(ss, ent->parent);
if (old_parent->valid)
{
/* Unlink from current parent */
PP_Unlink(ent);
}
PP_EntKey ent_id = ent->key;
PP_EntKey last_child_id = parent->last;
PP_Ent *last_child = PP_EntFromKey(ss, last_child_id);
if (last_child->valid)
{
ent->prev = last_child_id;
last_child->next = ent_id;
}
else
{
parent->first = ent_id;
}
parent->last = ent_id;
if (parent->is_root)
{
ent->is_top = 1;
ent->top = ent_id;
}
else
{
ent->top = parent->top;
}
ent->parent = parent->key;
}
/* NOTE: PP_Ent will be dangling after calling this, should re-link to root ent. */
void PP_Unlink(PP_Ent *ent)
{
PP_Snapshot *ss = ent->ss;
PP_EntKey parent_id = ent->parent;
PP_Ent *parent = PP_EntFromKey(ss, parent_id);
PP_Ent *prev = PP_EntFromKey(ss, ent->prev);
PP_Ent *next = PP_EntFromKey(ss, ent->next);
/* Unlink from parent & siblings */
if (prev->valid)
{
prev->next = next->key;
}
else
{
parent->first = next->key;
}
if (next->valid)
{
next->prev = prev->key;
}
else
{
parent->last = prev->key;
}
ent->prev = PP_NilEntKey;
ent->next = PP_NilEntKey;
}
////////////////////////////////////////////////////////////
//~ Xform
void PP_MarkChildXformsDirty(PP_Snapshot *ss, PP_Ent *ent)
{
for (PP_Ent *child = PP_EntFromKey(ss, ent->first); child->valid; child = PP_EntFromKey(ss, child->next))
{
if (child->_is_xform_dirty)
{
break;
}
else
{
child->_is_xform_dirty = 1;
PP_MarkChildXformsDirty(ss, child);
}
}
}
Xform PP_XformFromEnt_(PP_Snapshot *ss, PP_Ent *ent)
{
Xform xf;
if (ent->_is_xform_dirty)
{
if (ent->is_top)
{
xf = ent->_local_xform;
}
else
{
PP_Ent *parent = PP_EntFromKey(ss, ent->parent);
xf = PP_XformFromEnt_(ss, parent);
xf = MulXform(xf, ent->_local_xform);
ent->_xform = xf;
ent->_is_xform_dirty = 0;
}
ent->_xform = xf;
ent->_is_xform_dirty = 0;
}
else
{
xf = ent->_xform;
}
return xf;
}
Xform PP_XformFromEnt(PP_Ent *ent)
{
Xform xf;
if (ent->_is_xform_dirty)
{
if (ent->is_top)
{
xf = ent->_local_xform;
}
else
{
PP_Snapshot *ss = ent->ss;
PP_Ent *parent = PP_EntFromKey(ss, ent->parent);
xf = PP_XformFromEnt_(ss, parent);
xf = MulXform(xf, ent->_local_xform);
ent->_xform = xf;
ent->_is_xform_dirty = 0;
}
ent->_xform = xf;
ent->_is_xform_dirty = 0;
}
else
{
xf = ent->_xform;
}
return xf;
}
Xform PP_LocalXformFromEnt(PP_Ent *ent)
{
return ent->_local_xform;
}
void PP_SetXform(PP_Ent *ent, Xform xf)
{
if (!EqXform(xf, ent->_xform))
{
PP_Snapshot *ss = ent->ss;
/* Update local xform */
if (ent->is_top)
{
ent->_local_xform = xf;
}
else
{
PP_Ent *parent = PP_EntFromKey(ss, ent->parent);
Xform parent_global = PP_XformFromEnt_(ss, parent);
ent->_local_xform = MulXform(InvertXform(parent_global), xf);
}
ent->_xform = xf;
ent->_is_xform_dirty = 0;
PP_MarkChildXformsDirty(ss, ent);
}
}
void PP_SetLocalXform(PP_Ent *ent, Xform xf)
{
if (!EqXform(xf, ent->_local_xform))
{
ent->_local_xform = xf;
ent->_is_xform_dirty = 1;
PP_MarkChildXformsDirty(ent->ss, ent);
}
}
////////////////////////////////////////////////////////////
//~ Movement
void PP_SetLinearVelocity(PP_Ent *ent, Vec2 velocity)
{
if (PP_HasProp(ent, PP_Prop_Kinematic) || PP_HasProp(ent, PP_Prop_Dynamic))
{
ent->linear_velocity = ClampVec2Len(velocity, SIM_MAX_LINEAR_VELOCITY);
}
}
void PP_SetAngularVelocity(PP_Ent *ent, f32 velocity)
{
if (PP_HasProp(ent, PP_Prop_Kinematic) || PP_HasProp(ent, PP_Prop_Dynamic))
{
ent->angular_velocity = ClampF32(velocity, -SIM_MAX_ANGULAR_VELOCITY, SIM_MAX_ANGULAR_VELOCITY);
}
}
void PP_ApplyLinearImpulse(PP_Ent *ent, Vec2 impulse, Vec2 point)
{
if (PP_HasProp(ent, PP_Prop_Dynamic))
{
Xform xf = PP_XformFromEnt(ent);
Vec2 center = xf.og;
f32 scale = AbsF32(DeterminantFromXform(xf));
f32 inv_mass = 1.f / (ent->mass_unscaled * scale);
f32 inv_inertia = 1.f / (ent->inertia_unscaled * scale);
Vec2 vcp = SubVec2(point, center);
PP_SetLinearVelocity(ent, AddVec2(ent->linear_velocity, MulVec2(impulse, inv_mass)));
PP_SetAngularVelocity(ent, WedgeVec2(vcp, impulse) * inv_inertia);
}
}
void PP_ApplyLinearImpulseToCenter(PP_Ent *ent, Vec2 impulse)
{
if (PP_HasProp(ent, PP_Prop_Dynamic))
{
Xform xf = PP_XformFromEnt(ent);
f32 scale = AbsF32(DeterminantFromXform(xf));
f32 inv_mass = 1.f / (ent->mass_unscaled * scale);
PP_SetLinearVelocity(ent, AddVec2(ent->linear_velocity, MulVec2(impulse, inv_mass)));
}
}
void PP_ApplyForceToCenter(PP_Ent *ent, Vec2 force)
{
if (PP_HasProp(ent, PP_Prop_Dynamic))
{
ent->force = AddVec2(ent->force, force);
}
}
void PP_ApplyAngularImpulse(PP_Ent *ent, f32 impulse)
{
if (PP_HasProp(ent, PP_Prop_Dynamic))
{
Xform xf = PP_XformFromEnt(ent);
f32 scale = AbsF32(DeterminantFromXform(xf));
f32 inv_inertia = 1.f / (ent->inertia_unscaled * scale);
PP_SetAngularVelocity(ent, ent->angular_velocity + impulse * inv_inertia);
}
}
void PP_ApplyTorque(PP_Ent *ent, f32 torque)
{
if (PP_HasProp(ent, PP_Prop_Dynamic))
{
ent->torque += torque;
}
}
////////////////////////////////////////////////////////////
//~ Tile
PP_Ent *PP_TileChunkFromChunkIndex(PP_Snapshot *ss, Vec2I32 chunk_index)
{
PP_EntKey chunk_id = PP_TileChunkKeyFromIndex(chunk_index);
PP_Ent *chunk_ent = PP_EntFromKey(ss, chunk_id);
return chunk_ent;
}
PP_Ent *PP_TileChunkFromWorldTileIndex(PP_Snapshot *ss, Vec2I32 world_tile_index)
{
Vec2I32 chunk_index = PP_TileChunkIndexFromWorldTileIndex(world_tile_index);
PP_Ent *chunk_ent = PP_TileChunkFromChunkIndex(ss, chunk_index);
return chunk_ent;
}
PP_TileKind PP_TileKindFromChunk(PP_Ent *chunk_ent, Vec2I32 local_tile_index)
{
PP_TileKind result = chunk_ent->tile_chunk_tiles[local_tile_index.x + (local_tile_index.y * SIM_TILES_PER_CHUNK_SQRT)];
return result;
}
////////////////////////////////////////////////////////////
//~ Lerp
void PP_LerpEnt(PP_Ent *e, PP_Ent *e0, PP_Ent *e1, f64 blend)
{
if (PP_IsValidAndActive(e0) && PP_IsValidAndActive(e1)
&& PP_EqEntKey(e0->key, e1->key)
&& e0->continuity_gen == e1->continuity_gen)
{
e->_local_xform = LerpXform(e0->_local_xform, e1->_local_xform, blend);
if (e->is_top)
{
/* TODO: Cache parent & child xforms in sim */
Xform e0_xf = PP_XformFromEnt(e0);
Xform e1_xf = PP_XformFromEnt(e1);
PP_SetXform(e, LerpXform(e0_xf, e1_xf, blend));
}
e->control_force = LerpF32(e0->control_force, e1->control_force, blend);
e->control_torque = LerpF32(e0->control_torque, e1->control_torque, blend);
e->linear_velocity = LerpVec2(e0->linear_velocity, e1->linear_velocity, blend);
e->angular_velocity = LerpAngleF32(e0->angular_velocity, e1->angular_velocity, blend);
e->control.move = LerpVec2(e0->control.move, e1->control.move, blend);
e->control.focus = LerpVec2(e0->control.focus, e1->control.focus, blend);
e->sprite_local_xform = LerpXform(e0->sprite_local_xform, e1->sprite_local_xform, blend);
e->animation_last_frame_change_time_ns = LerpI64(e0->animation_last_frame_change_time_ns, e1->animation_last_frame_change_time_ns, (f64)blend);
e->animation_frame = (u32)RoundF32ToI32(LerpF32(e0->animation_frame, e1->animation_frame, blend));
e->camera_quad_xform = LerpXform(e0->camera_quad_xform, e1->camera_quad_xform, blend);
e->camera_xform_target = LerpXform(e0->camera_xform_target, e1->camera_xform_target, blend);
e->shake = LerpF32(e0->shake, e1->shake, blend);
e->tracer_gradient_start = LerpVec2(e0->tracer_gradient_start, e1->tracer_gradient_start, blend);
e->tracer_gradient_end = LerpVec2(e0->tracer_gradient_end, e1->tracer_gradient_end, blend);
}
}
////////////////////////////////////////////////////////////
//~ Sync
/* Walks a local & remote ent tree and allocates any missing net dst ents from remote src ents */
void PP_CreateMissingEntsFromSnapshots(PP_Ent *local_parent, PP_Ent *remote, PP_EntKey remote_player)
{
__prof;
if (PP_HasProp(remote, PP_Prop_SyncSrc))
{
PP_Snapshot *local_ss = local_parent->ss;
PP_Snapshot *remote_ss = remote->ss;
PP_EntKey key = remote->key;
PP_Ent *local_ent = PP_EntFromKey(local_ss, key);
if (!local_ent->valid)
{
local_ent = PP_AcquireSyncDstEnt(local_parent, key, remote_player);
}
for (PP_Ent *remote_child = PP_EntFromKey(remote_ss, remote->first); remote_child->valid; remote_child = PP_EntFromKey(remote_ss, remote_child->next))
{
PP_CreateMissingEntsFromSnapshots(local_ent, remote_child, remote_player);
}
}
}
/* Copies data between two synced entities */
void PP_SyncEnt(PP_Ent *local, PP_Ent *remote)
{
PP_Ent old = *local;
CopyStruct(local, remote);
/* Why would 2 ents w/ different uids ever be synced? */
Assert(PP_EqEntKey(local->key, old.key));
local->ss = old.ss;
local->key = old.key;
/* Keep local tree */
local->parent = old.parent;
local->prev = old.prev;
local->next = old.next;
local->first = old.first;
local->last = old.last;
local->top = old.top;
local->owner = old.owner;
/* Keep indices */
local->next_in_id_bin = old.next_in_id_bin;
local->prev_in_id_bin = old.prev_in_id_bin;
local->next_free = old.next_free;
PP_DisableProp(local, PP_Prop_SyncSrc);
PP_EnableProp(local, PP_Prop_SyncDst);
}
////////////////////////////////////////////////////////////
//~ Encode
void PP_EncodeEnt(BB_Writer *bw, PP_Ent *e0, PP_Ent *e1)
{
PP_Snapshot *ss = e1->ss;
/* FIXME: Things like xforms need to be retreived manually rather than memcopied. */
/* TODO: Granular delta encoding */
u64 pos = 0;
e1->ss = e0->ss;
while (pos < sizeof(*e1))
{
u64 chunk_size = MinU64(pos + 8, sizeof(*e1)) - pos;
u8 *chunk0 = (u8 *)e0 + pos;
u8 *chunk1 = (u8 *)e1 + pos;
if (BB_WriteBit(bw, !EqBytes(chunk0, chunk1, chunk_size)))
{
u64 bits = 0;
CopyBytes(&bits, chunk1, chunk_size);
BB_WriteUBits(bw, bits, 64);
}
pos += 8;
}
e1->ss = ss;
}
////////////////////////////////////////////////////////////
//~ Decode
void PP_DecodeEnt(BB_Reader *br, PP_Ent *e)
{
PP_Snapshot *old_ss = e->ss;
{
u64 pos = 0;
while (pos < sizeof(*e))
{
u8 *chunk = (u8 *)e + pos;
if (BB_ReadBit(br))
{
u64 chunk_size = MinU64(pos + 8, sizeof(*e)) - pos;
u64 bits = BB_ReadUBits(br, 64);
CopyBytes(chunk, &bits, chunk_size);
}
pos += 8;
}
}
e->ss = old_ss;
}

View File

@ -1,568 +0,0 @@
////////////////////////////////////////////////////////////
//~ Ent props
Enum(PP_Prop)
{
PP_Prop_Active,
PP_Prop_Release,
PP_Prop_SyncSrc, /* This entity is networked to other clients */
PP_Prop_SyncDst, /* This entity is not locally created, and will sync with incoming net src ents */
PP_Prop_Player,
PP_Prop_IsMaster,
PP_Prop_Cmd,
PP_Prop_TileChunk,
PP_Prop_Wall,
/* Physics collision */
PP_Prop_Sensor, /* This entity's collisions generate contacts */
PP_Prop_Solid, /* This entity's collisions generate contacts to be solved by the physics system (overrides PP_Prop_Sensor) */
PP_Prop_Toi, /* This entity's collisions are processed using TOI (time of impact) for precise collisions */
/* Physics movement */
PP_Prop_Kinematic, /* This entity reacts to velocity */
PP_Prop_Dynamic, /* This entity reacts to velocity and forces (overrides PP_Prop_Kinematic) */
PP_Prop_Controlled,
PP_Prop_CollisionDebug,
PP_Prop_ContactConstraint,
PP_Prop_MotorJoint,
PP_Prop_MouseJoint,
PP_Prop_WeldJoint,
PP_Prop_Camera,
PP_Prop_ActiveCamera,
PP_Prop_Bullet,
PP_Prop_Smg,
PP_Prop_Launcher,
PP_Prop_Chucker,
PP_Prop_ChuckerZone,
PP_Prop_Explosion,
PP_Prop_Tracer,
PP_Prop_Quake,
PP_Prop_Attached,
/* Test props */
PP_Prop_Test,
PP_Prop_SoundEmitterTest,
PP_Prop_LightTest,
PP_Prop_COUNT
};
////////////////////////////////////////////////////////////
//~ Ent
Struct(PP_Ent)
{
PP_Snapshot *ss;
//- Metadata
b32 valid; /* Is this ent allocated in memory that can be written to (can always be read) */
PP_EntKey key;
u64 props[(PP_Prop_COUNT + 63) / 64];
u64 continuity_gen;
/* Is this the root ent */
b32 is_root;
/* Is ent a child of the root ent */
b32 is_top;
/* The key of the top level parent of the ent tree (if ent is top then this point to itself) */
PP_EntKey top;
/* Tree */
PP_EntKey parent;
PP_EntKey next;
PP_EntKey prev;
PP_EntKey first;
PP_EntKey last;
/* Lists keyed by index in snapshot ent array */
u32 next_in_id_bin;
u32 prev_in_id_bin;
u32 next_free;
//- Sync
/* PP_Prop_SyncSrc */
/* PP_Prop_SyncDst */
/* Key of the player that owns simulation for this entity */
PP_EntKey owner;
/* Key of the player that should predict simulation of this this entity locally */
PP_EntKey predictor;
//- Position
/* Use xform getters & setters to access. */
Xform _local_xform; /* Transform in relation to parent ent (or the world if ent has no parent) */
Xform _xform; /* Calculated from ent tree */
b32 _is_xform_dirty;
//- Activation
/* If 0, the ent will auto activate at start of next tick if not already active. */
u64 activation_tick;
//- Layer
i32 layer;
i32 final_layer; /* Calculated each tick from ent tree */
//- Cmd
/* PP_Prop_Cmd */
PP_CmdKind cmd_kind;
PP_EntKey cmd_player;
/* FIXME: Lerp */
/* Control cmd */
PP_ControlData cmd_control;
PP_EntKey cmd_control_hovered_ent;
/* Chat cmd */
//String cmd_chat_msg;
//- Chat
/* PP_Prop_Chat */
PP_EntKey chat_player;
//String chat_msg;
//- Tile
/* PP_Prop_TileChunk */
/* FIXME: Move out of here */
u8 tile_chunk_tiles[SIM_TILES_PER_CHUNK_SQRT * SIM_TILES_PER_CHUNK_SQRT];
Vec2I32 tile_chunk_index;
//- Client
/* PP_Prop_Player */
/* FIXME: Lerp */
PP_ClientKey player_client_key; /* The client key on the master sim's machine */
PP_ControlData player_control;
Vec2 player_cursor_pos;
PP_EntKey player_hovered_ent;
PP_EntKey player_control_ent;
PP_EntKey player_camera_ent;
PP_EntKey player_dbg_drag_joint_ent;
b32 player_dbg_drag_start;
b32 player_dbg_drag_stop;
/* Client round-trip-time to server */
i64 player_last_rtt_ns;
f64 player_average_rtt_seconds;
//- Collider
Vec2 collision_dir; /* If set, then only collisions coming from this direction will generate contacts (used for walls to prevent ghost collisions) */
CLD_Shape local_collider;
#if COLLIDER_DEBUG
ContactDebugData collision_debug_data;
#endif
PP_SpaceEntryKey space_key;
//- Constraints / joints
/* PP_Prop_ContactConstraint */
PP_ContactConstraint contact_constraint_data;
/* PP_Prop_MotorJoint */
PP_MotorJoint motor_joint_data;
/* PP_Prop_MouseJoint */
PP_MouseJoint mouse_joint_data;
/* PP_Prop_WeldJoint */
PP_WeldJoint weld_joint_data;
//- Control
/* PP_Prop_Controlled */
PP_EntKey controlling_player;
f32 control_force; /* How much force is applied to achieve desired control movement */
f32 control_force_max_speed; /* Maximum linear velocity achieved by force (m/s) */
f32 control_torque; /* How much torque is applied when turning towards desired focus */
PP_ControlData control;
PP_EntKey move_joint;
PP_EntKey aim_joint;
//- Physics
/* PP_Prop_Dynamic */
//f32 density; /* Density in kg/m^2 */
f32 friction;
f32 mass_unscaled; /* Mass of ent in kg before any transformations */
f32 inertia_unscaled; /* Inertia of ent in kg*m^2 before any transformations */
PP_EntKey ground_friction_joint;
f32 linear_ground_friction;
f32 angular_ground_friction;
/* Use PP_SetLinearVelocity & PP_SetAngularVelocity to set */
Vec2 linear_velocity; /* m/s */
f32 angular_velocity; /* rad/s */
Vec2 force;
f32 torque;
f32 linear_damping;
f32 angular_damping;
//- Sprite
ResourceKey sprite;
SPR_SpanKey sprite_span_key;
u32 sprite_tint;
Vec3 sprite_emittance;
SPR_SliceKey sprite_collider_slice_key; /* Collider will sync to bounds of this slice if set */
Xform sprite_local_xform; /* Sprite transform in relation to ent */
//- Animation
/* PP_Prop_Animating */
i64 animation_last_frame_change_time_ns;
u32 animation_frame;
//- Attachment
/* PP_Prop_Attached */
/* Slice on the parent ent's sprite to attach to */
SPR_SliceKey attach_slice_key;
//- Equip
PP_EntKey equipped;
//- Chucker
/* PP_Prop_Chucker */
PP_EntKey chucker_zone;
PP_EntKey chucker_joint;
//- Chucker zone
/* PP_Prop_ChuckerZone */
PP_EntKey chucker_zone_ent;
u64 chucker_zone_ent_tick;
//- Triggerable
i32 num_primary_triggers;
i32 num_secondary_triggers;
f32 primary_fire_delay;
f32 secondary_fire_delay;
i64 last_primary_fire_ns;
i64 last_secondary_fire_ns;
//- Trigger
/* How many times has this trigger been triggered this tick */
i64 triggered_count;
/* Other triggers to activate when this entity has been triggered */
//PP_EntKey trigger_out_left;
//PP_EntKey trigger_out_right;
//- Bullet
PP_EntKey bullet_src;
PP_EntKey bullet_tracer;
Vec2 bullet_src_pos;
Vec2 bullet_src_dir;
f32 bullet_launch_velocity;
f32 bullet_knockback;
f32 bullet_explosion_strength;
f32 bullet_explosion_radius;
b32 bullet_has_hit;
//- Explosion
f32 explosion_strength;
f32 explosion_radius;
//- Tracer
/* PP_Prop_Tracer */
Vec2 tracer_start;
Vec2 tracer_start_velocity;
f32 tracer_fade_duration; /* Time for tracer to fade from opacity of 1 to 0 */
/* calculated each frame */
Vec2 tracer_gradient_start;
Vec2 tracer_gradient_end;
//- Quake
/* PP_Prop_Quake */
f32 quake_intensity;
f32 quake_frequency;
f32 quake_fade; /* How much intensity to lose per second */
//- Testing
/* PP_Prop_Test */
b32 test_initialized;
Xform test_start_local_xform;
Xform test_start_sprite_xform;
/* PP_Prop_SoundEmitterTest */
// String sound_name;
// MIX_TrackDesc sound_desc;
// MIX_Handle sound_handle;
//- Camera
/* PP_Prop_Camera */
PP_EntKey camera_follow;
Xform camera_quad_xform;
f32 camera_lerp; /* Rate at which camera xform approaches target xform */
u32 camera_lerp_continuity_gen;
Xform camera_xform_target; /* Calculated from camera_follow */
u32 camera_applied_lerp_continuity_gen_plus_one; /* Calculated */
f32 shake;
};
Struct(PP_EntArray)
{
PP_Ent *ents;
u64 count;
};
Struct(PP_PropArray)
{
PP_Prop *props;
u64 count;
};
Struct(PP_EntBin)
{
u32 first;
u32 last;
};
Inline PP_Ent *PP_NilEnt(void)
{
extern Readonly PP_Ent **PP_nil_ent;
return *PP_nil_ent;
}
////////////////////////////////////////////////////////////
//~ Key types
#define PP_NilEntKey ((PP_EntKey) { UID(0, 0) })
#define PP_RootEntKey ((PP_EntKey) { UID(0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa) })
/* Key magic number constants (to be used in conjunction with ent ids in deterministic key combinations) */
#define PP_TileChunkBasisUid (UID(0x3ce42de071dd226b, 0x9b566f7df30c813a))
#define PP_ContactBasisUid (UID(0x6a2a5d2dbecf534f, 0x0a8ca7c372a015af))
#define PP_CollisionDebugBasisUid (UID(0x302c01182013bb02, 0x570bd270399d11a5))
////////////////////////////////////////////////////////////
//~ Key helpers
Inline b32 PP_EqEntKey(PP_EntKey a, PP_EntKey b)
{
return EqUid(a.uid, b.uid);
}
Inline b32 PP_IsNilEntKey(PP_EntKey key)
{
return EqUid(key.uid, PP_NilEntKey.uid);
}
////////////////////////////////////////////////////////////
//~ Property helpers
Inline void PP_EnableProp(PP_Ent *ent, PP_Prop prop)
{
u64 index = prop / 64;
u64 bit = prop % 64;
ent->props[index] |= ((u64)1 << bit);
}
Inline void PP_DisableProp(PP_Ent *ent, PP_Prop prop)
{
u64 index = prop / 64;
u64 bit = prop % 64;
ent->props[index] &= ~((u64)1 << bit);
}
Inline b32 PP_HasProp(PP_Ent *ent, PP_Prop prop)
{
u64 index = prop / 64;
u64 bit = prop % 64;
return !!(ent->props[index] & ((u64)1 << bit));
}
Inline b32 PP_IsValidAndActive(PP_Ent *ent)
{
return ent->valid && PP_HasProp(ent, PP_Prop_Active);
}
Inline b32 PP_ShouldPredict(PP_Ent *ent)
{
return PP_EqEntKey(ent->predictor, ent->ss->local_player);
}
Inline b32 PP_IsOwner(PP_Ent *ent)
{
return PP_EqEntKey(ent->owner, ent->ss->local_player);
}
Inline b32 PP_ShouldSimulate(PP_Ent *ent)
{
b32 result = 0;
if (PP_IsValidAndActive(ent))
{
result = 1;
if (PP_HasProp(ent, PP_Prop_SyncDst))
{
PP_EntKey local_player = ent->ss->local_player;
result = PP_EqEntKey(local_player, ent->owner) || PP_EqEntKey(local_player, ent->predictor);
}
}
return result;
}
////////////////////////////////////////////////////////////
//~ Acquire operations
PP_Ent *PP_AcquireRawEnt(PP_Snapshot *ss, PP_Ent *parent, PP_EntKey key);
PP_Ent *PP_AcquireLocalEnt(PP_Ent *parent);
PP_Ent *PP_AcquireLocalEntWithKey(PP_Ent *parent, PP_EntKey key);
PP_Ent *PP_AcquireSyncSrcEnt(PP_Ent *parent);
PP_Ent *PP_AcquireSyncSrcEntWithKey(PP_Ent *parent, PP_EntKey key);
PP_Ent *PP_AcquireSyncDstEnt(PP_Ent *parent, PP_EntKey ent_id, PP_EntKey owner_id);
////////////////////////////////////////////////////////////
//~ Release operations
void PP_ReleaseRawEnt(PP_Ent *ent);
void PP_Release(PP_Ent *ent);
void PP_ReleaseAllWithProp(PP_Snapshot *ss, PP_Prop prop);
////////////////////////////////////////////////////////////
//~ Activate operations
void PP_ActivateEnt(PP_Ent *ent, u64 current_tick);
////////////////////////////////////////////////////////////
//~ Key operations
u32 PP_IndexFromEnt(PP_Snapshot *ss, PP_Ent *ent);
PP_Ent *PP_EntFromIndex(PP_Snapshot *ss, u32 index);
PP_EntBin *PP_EntBinFromKey(PP_Snapshot *ss, PP_EntKey key);
void PP_SetEntKey(PP_Ent *ent, PP_EntKey key);
PP_Ent *PP_EntFromKey(PP_Snapshot *ss, PP_EntKey key);
PP_EntKey PP_RandomKey(void);
PP_EntKey PP_ContactConstraintKeyFromContactingKeys(PP_EntKey player_id, PP_EntKey id0, PP_EntKey id1);
PP_EntKey PP_CollisionDebugKeyFromKeys(PP_EntKey player_id, PP_EntKey id0, PP_EntKey id1);
PP_EntKey PP_TileChunkKeyFromIndex(Vec2I32 chunk_start);
////////////////////////////////////////////////////////////
//~ Query operations
PP_Ent *PP_FirstWithProp(PP_Snapshot *ss, PP_Prop prop);
PP_Ent *PP_FirstWithAllProps(PP_Snapshot *ss, PP_PropArray props);
////////////////////////////////////////////////////////////
//~ Tree operations
void PP_Link(PP_Ent *parent, PP_Ent *child);
void PP_Unlink(PP_Ent *ent);
////////////////////////////////////////////////////////////
//~ Xform operations
void PP_MarkChildXformsDirty(PP_Snapshot *ss, PP_Ent *ent);
Xform PP_XformFromEnt_(PP_Snapshot *ss, PP_Ent *ent);
Xform PP_XformFromEnt(PP_Ent *ent);
Xform PP_LocalXformFromEnt(PP_Ent *ent);
void PP_SetXform(PP_Ent *ent, Xform xf);
void PP_SetLocalXform(PP_Ent *ent, Xform xf);
////////////////////////////////////////////////////////////
//~ Movement operations
void PP_SetLinearVelocity(PP_Ent *ent, Vec2 velocity);
void PP_SetAngularVelocity(PP_Ent *ent, f32 velocity);
void PP_ApplyLinearImpulse(PP_Ent *ent, Vec2 impulse, Vec2 world_point);
void PP_ApplyLinearImpulseToCenter(PP_Ent *ent, Vec2 impulse);
void PP_ApplyForceToCenter(PP_Ent *ent, Vec2 force);
void PP_ApplyAngularImpulse(PP_Ent *ent, f32 impulse);
void PP_ApplyTorque(PP_Ent *ent, f32 torque);
////////////////////////////////////////////////////////////
//~ Tile operations
PP_Ent *PP_TileChunkFromChunkIndex(PP_Snapshot *ss, Vec2I32 chunk_index);
PP_Ent *PP_TileChunkFromWorldTileIndex(PP_Snapshot *ss, Vec2I32 world_tile_index);
PP_TileKind PP_TileKindFromChunk(PP_Ent *chunk_ent, Vec2I32 local_tile_index);
////////////////////////////////////////////////////////////
//~ Lerp operations
void PP_LerpEnt(PP_Ent *e, PP_Ent *e0, PP_Ent *e1, f64 blend);
////////////////////////////////////////////////////////////
//~ Sync operations
void PP_CreateMissingEntsFromSnapshots(PP_Ent *local_parent, PP_Ent *remote, PP_EntKey remote_player);
void PP_SyncEnt(PP_Ent *local, PP_Ent *remote);
////////////////////////////////////////////////////////////
//~ Encode
void PP_EncodeEnt(BB_Writer *bw, PP_Ent *e0, PP_Ent *e1);
////////////////////////////////////////////////////////////
//~ Decode
void PP_DecodeEnt(BB_Reader *br, PP_Ent *e);

File diff suppressed because it is too large Load Diff

View File

@ -1,266 +0,0 @@
////////////////////////////////////////////////////////////
//~ Collision data types
#define PP_ContactSpringHz 25
#define PP_ContactSpringDamp 10
Struct(PP_CollisionData)
{
PP_EntKey e0;
PP_EntKey e1;
Vec2 point;
Vec2 normal; /* Normal of the collision from e0 to e1 */
Vec2 vrel; /* Relative velocity at point of collision */
b32 is_start; /* Did this collision just begin */
f32 dt; /* How much time elapsed in the step when this event occurred (this will equal the physics timestep unless an early time of impact occurred) */
};
/* Callback can return 1 to prevent the physics system from resolving */
struct PP_SimStepCtx;
#define PP_CollisionCallbackFuncDef(name, arg_collision_data, arg_sim_step_ctx) b32 name(PP_CollisionData *arg_collision_data, struct PP_SimStepCtx *arg_sim_step_ctx)
typedef PP_CollisionCallbackFuncDef(PP_CollisionCallbackFunc, collision_data, ctx);
////////////////////////////////////////////////////////////
//~ Step ctx
/* Structure containing data used for a single physics step */
Struct(PP_PhysStepCtx)
{
struct PP_SimStepCtx *sim_step_ctx;
PP_CollisionCallbackFunc *collision_callback;
};
////////////////////////////////////////////////////////////
//~ Contact types
Struct(PP_ContactPoint)
{
// Contact point relative to the center of each entity.
//
// NOTE: We use fixed (non-rotated) points relative to the entities
// rather than points fully in local space because contact manifolds
// shouldn't really be affected by rotation accross substeps
// (imagine re-building the manifold of a rotated shape, it would still be
// on the same side of the shape that it originally occured on)
Vec2 vcp0;
Vec2 vcp1;
u32 id; /* ID generated during clipping */
f32 starting_separation; /* How far are original points along normal */
f32 normal_impulse; /* Accumulated impulse along normal */
f32 tangent_impulse; /* Accumulated impulse along tangent */
f32 inv_normal_mass;
f32 inv_tangent_mass;
/* Debugging */
#if IsDeveloperModeEnabled
Vec2 dbg_pt;
#endif
};
Struct(PP_ContactConstraint)
{
u64 last_phys_iteration; /* To avoid checking collisions for the same constraint twice in one tick */
b32 skip_solve;
b32 wrong_dir;
PP_EntKey e0;
PP_EntKey e1;
f32 inv_m0;
f32 inv_m1;
f32 inv_i0;
f32 inv_i1;
Vec2 normal; /* Normal vector of collision from e0 -> e1 */
u64 last_iteration;
PP_ContactPoint points[2];
u32 num_points;
f32 friction;
f32 pushout_velocity;
};
Struct(PP_ContactDebugData)
{
PP_EntKey e0;
PP_EntKey e1;
CLD_CollisionData collision_result;
PP_ContactPoint points[2];
u32 num_points;
Vec2 closest0;
Vec2 closest1;
Xform xf0;
Xform xf1;
};
////////////////////////////////////////////////////////////
//~ Motor joint types
Struct(PP_MotorJointDesc)
{
PP_EntKey e0;
PP_EntKey e1;
f32 correction_rate;
f32 max_force;
f32 max_torque;
};
Struct(PP_MotorJoint)
{
PP_EntKey e0;
PP_EntKey e1;
f32 correction_rate;
f32 max_force;
f32 max_torque;
f32 inv_m0;
f32 inv_m1;
f32 inv_i0;
f32 inv_i1;
Vec2 linear_impulse;
f32 angular_impulse;
Vec2 point_local_e0;
Vec2 point_local_e1;
Xform linear_mass_xf;
f32 angular_mass;
};
////////////////////////////////////////////////////////////
//~ Mouse joint types
Struct(PP_MouseJointDesc)
{
PP_EntKey target;
Vec2 point_local_start;
Vec2 point_end;
f32 linear_spring_hz;
f32 linear_spring_damp;
f32 angular_spring_hz;
f32 angular_spring_damp;
f32 max_force;
};
Struct(PP_MouseJoint)
{
PP_EntKey target;
Vec2 point_local_start;
Vec2 point_end;
f32 linear_spring_hz;
f32 linear_spring_damp;
f32 angular_spring_hz;
f32 angular_spring_damp;
f32 max_force;
f32 inv_m;
f32 inv_i;
Vec2 linear_impulse;
f32 angular_impulse;
Xform linear_mass_xf;
};
////////////////////////////////////////////////////////////
//~ Weld joint types
Struct(PP_WeldJointDesc)
{
PP_EntKey e0;
PP_EntKey e1;
// The xform that transforms a point in e0's space into the desired e1 space
// (IE `xf` * VEC2(0, 0) should evaluate to the local point that e1's origin will lie)
Xform xf;
f32 linear_spring_hz;
f32 linear_spring_damp;
f32 angular_spring_hz;
f32 angular_spring_damp;
};
Struct(PP_WeldJoint)
{
PP_EntKey e0;
PP_EntKey e1;
Xform xf0_to_xf1;
f32 linear_spring_hz;
f32 linear_spring_damp;
f32 angular_spring_hz;
f32 angular_spring_damp;
f32 inv_m0;
f32 inv_m1;
f32 inv_i0;
f32 inv_i1;
Vec2 linear_impulse0;
Vec2 linear_impulse1;
f32 angular_impulse0;
f32 angular_impulse1;
};
////////////////////////////////////////////////////////////
//~ Contact operations
b32 PP_CanEntsContact(struct PP_Ent *e0, struct PP_Ent *e1);
void PP_CreateAndUpdateContacts(PP_PhysStepCtx *ctx, f32 elapsed_dt, u64 phys_iteration);
void PP_PrepareContacts(PP_PhysStepCtx *ctx, u64 phys_iteration);
void PP_WarmStartContacts(PP_PhysStepCtx *ctx);
void PP_SolveContacts(PP_PhysStepCtx *ctx, f32 dt, b32 apply_bias);
////////////////////////////////////////////////////////////
//~ Motor joint operations
PP_MotorJointDesc PP_CreateMotorJointDef(void);
PP_MotorJoint PP_MotorJointFromDef(PP_MotorJointDesc def);
void PP_PrepareMotorJoints(PP_PhysStepCtx *ctx);
void PP_WarmStartMotorJoints(PP_PhysStepCtx *ctx);
void PP_SolveMotorJoints(PP_PhysStepCtx *ctx, f32 dt);
////////////////////////////////////////////////////////////
//~ Mouse joint operations
PP_MouseJointDesc PP_CreateMouseJointDef(void);
PP_MouseJoint PP_MouseJointFromDef(PP_MouseJointDesc def);
void PP_PrepareMouseJoints(PP_PhysStepCtx *ctx);
void PP_WarmStartMouseJoints(PP_PhysStepCtx *ctx);
void PP_SolveMouseJoints(PP_PhysStepCtx *ctx, f32 dt);
////////////////////////////////////////////////////////////
//~ Weld joint operations
PP_WeldJointDesc PP_CreateWeldJointDef(void);
PP_WeldJoint PP_WeldJointFromDef(PP_WeldJointDesc def);
void PP_PrepareWeldJoints(PP_PhysStepCtx *ctx);
void PP_WarmStartWeldJoints(PP_PhysStepCtx *ctx);
void PP_SolveWeldJoints(PP_PhysStepCtx *ctx, f32 dt);
////////////////////////////////////////////////////////////
//~ Integration operations
Xform PP_GetDerivedEntXform(struct PP_Ent *ent, f32 dt);
void PP_IntegrateForces(PP_PhysStepCtx *ctx, f32 dt);
void PP_IntegrateVelocities(PP_PhysStepCtx *ctx, f32 dt);
////////////////////////////////////////////////////////////
//~ Time of impact operations
f32 PP_DetermineEarliestToi(PP_PhysStepCtx *ctx, f32 step_dt, f32 tolerance, u32 max_iterations);
////////////////////////////////////////////////////////////
//~ Spatial operations
void PP_UpdateAabbs(PP_PhysStepCtx *ctx);
////////////////////////////////////////////////////////////
//~ Step
void PP_StepPhys(PP_PhysStepCtx *ctx, f32 timestep);

BIN
src/pp_old/pp_res/font/fixedsys.ttf (Stored with Git LFS)

Binary file not shown.

BIN
src/pp_old/pp_res/font/roboto-med.ttf (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

BIN
src/pp_old/pp_res/sprite/blood.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp_old/pp_res/sprite/box.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp_old/pp_res/sprite/box_rounded.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp_old/pp_res/sprite/bullet.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp_old/pp_res/sprite/crosshair.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp_old/pp_res/sprite/gun.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp_old/pp_res/sprite/tile.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp_old/pp_res/sprite/tim.ase (Stored with Git LFS)

Binary file not shown.

View File

@ -1,935 +0,0 @@
//
// Sim hierarchy is as follows:
//
// PP_Client store -> clients -> snapshots -> ents
//
// A client store holds clients, which can be retrieved by client key or by a host channel id (if one is assigned).
//
// A client holds snapshots, which can be retrieved by tick number.
// - The snapshots stored in clients & the contents of those snapshots are determined from data transmitted by the client.
// - Different kinds of clients will transmit different subsets of snapshot data (e.g. a master client will transmit most ent state, while slave clients may just transmit 1 or more command ents)
// - A client will never hold more than one snapshot for the same tick (e.g. a client will never have 2 snapshots at tick 5)
//
// A snapshot holds the ent tree for a particular tick, in which ents can be retrieved by ent key.
// - A tick is the quantized time step that all clients implicitly conform to.
//
// An ent is the smallest unit of simulation state.
// - It is assigned a 128 bit unique identifer generated at allocation time.
// - This key is used to refer to other ents in the tree and to sync ents accross clients.
// - This key is usually random, but can be deterministic under certain conditions.
// - For example, instead of storing a contact constraint lookup table, contact constraints are
// retrieved by searching for the ent with the key resulting from combining the ent keys of the
// two contacting entities (plus a unique 'basis' ent key used for all contact constraints).
// - The ent also implicitly has a 32 bit index, which is just its offset from the start of the entity array in the local snapshot.
// - Since index is based on offset which remains stable regardless of snapshot memory location, it
// is used when working in contexts where key is irrelevant.
//
////////////////////////////////////////////////////////////
//~ Startup
PP_SharedSimCtx PP_shared_sim_ctx = ZI;
Readonly PP_ClientStore **PP_nil_client_store = &PP_shared_sim_ctx.nil_client_store;
Readonly PP_Client **PP_nil_client = &PP_shared_sim_ctx.nil_client;
Readonly PP_Snapshot **PP_nil_snapshot = &PP_shared_sim_ctx.nil_snapshot;
Readonly PP_Ent **PP_nil_ent = &PP_shared_sim_ctx.nil_ent;
void PP_StartupSim(void)
{
__prof;
PP_SharedSimCtx *g = &PP_shared_sim_ctx;
g->nil_arena = AcquireArena(Gibi(1));
/* Nil client store */
g->nil_client_store = PushStruct(g->nil_arena, PP_ClientStore);
g->nil_client_store->valid = 0;
/* Nil client */
g->nil_client = PushStruct(g->nil_arena, PP_Client);
g->nil_client->valid = 0;
g->nil_client->store = PP_NilClientStore();
/* Nil snapshot */
g->nil_snapshot = PushStruct(g->nil_arena, PP_Snapshot);
g->nil_snapshot->valid = 0;
g->nil_snapshot->client = PP_NilClient();
/* Nil ent */
g->nil_ent = PushStruct(g->nil_arena, PP_Ent);
g->nil_ent->ss = PP_NilSnapshot();
g->nil_ent->valid = 0;
g->nil_ent->key = PP_NilEntKey;
g->nil_ent->_local_xform = XformIdentity;
g->nil_ent->_xform = XformIdentity;
g->nil_ent->_is_xform_dirty = 0;
g->nil_ent->friction = 0.5f;
g->nil_ent->mass_unscaled = 1;
g->nil_ent->inertia_unscaled = 1;
g->nil_ent->sprite_local_xform = XformIdentity;
g->nil_ent->sprite_tint = Color_White;
/* Lock nil arena */
SetArenaReadonly(g->nil_arena);
}
////////////////////////////////////////////////////////////
//~ Acquire client store
PP_ClientStore *PP_AcquireClientStore(void)
{
__prof;
PP_ClientStore *store;
{
Arena *arena = AcquireArena(Gibi(64));
store = PushStruct(arena, PP_ClientStore);
store->arena = arena;
}
store->valid = 1;
store->num_client_lookup_bins = PP_ClientLookupBinsCount;
store->client_lookup_bins = PushStructs(store->arena, PP_ClientLookupBin, store->num_client_lookup_bins);
store->clients_arena = AcquireArena(Gibi(64));
store->clients = PushDry(store->clients_arena, PP_Client);
return store;
}
void PP_ReleaseClientStore(PP_ClientStore *store)
{
__prof;
for (u64 i = 0; i < store->num_clients_reserved; ++i)
{
PP_Client *client = &store->clients[i];
if (client->valid)
{
PP_ReleaseClient(client);
}
}
ReleaseArena(store->clients_arena);
ReleaseArena(store->arena);
}
////////////////////////////////////////////////////////////
//~ Acquire client
PP_Client *PP_AcquireClient(PP_ClientStore *store)
{
PP_ClientKey key = ZI;
PP_Client *client = PP_ClientFromKey(store, store->first_free_client);
if (client->valid)
{
store->first_free_client = client->next_free;
key = client->key;
++key.gen;
}
else
{
client = PushStructNoZero(store->clients_arena, PP_Client);
key.gen = 1;
key.idx = store->num_clients_reserved;
++store->num_clients_reserved;
}
++store->num_clients_allocated;
*client = *PP_NilClient();
client->store = store;
client->valid = 1;
client->key = key;
client->snapshots_arena = AcquireArena(Gibi(8));
client->num_snapshot_lookup_bins = PP_TickLookupBinsCount;
client->snapshot_lookup_bins = PushStructs(client->snapshots_arena, PP_SnapshotLookupBin, client->num_snapshot_lookup_bins);
return client;
}
void PP_ReleaseClient(PP_Client *client)
{
/* Release internal snapshot memory */
for (u64 i = 0; i < client->num_snapshot_lookup_bins; ++i)
{
PP_SnapshotLookupBin *bin = &client->snapshot_lookup_bins[i];
PP_Snapshot *ss = bin->first;
while (ss)
{
PP_Snapshot *next = ss->next_in_bin;
ReleaseArena(ss->ents_arena);
ReleaseArena(ss->arena);
ss = next;
}
}
/* Remove from channel lookup */
PP_SetClientChannelId(client, N_NilChannelId);
/* Release client */
PP_ClientStore *store = client->store;
client->valid = 0;
client->next_free = store->first_free_client;
store->first_free_client = client->key;
--store->num_clients_allocated;
++client->key.gen;
ReleaseArena(client->snapshots_arena);
}
////////////////////////////////////////////////////////////
//~ Client lookup
u64 PP_ClientChannelHashFromChannelId(N_ChannelId channel_id)
{
return HashFnv64(Fnv64Basis, StringFromStruct(&channel_id));
}
void PP_SetClientChannelId(PP_Client *client, N_ChannelId channel_id)
{
PP_ClientStore *store = client->store;
N_ChannelId old_channel_id = client->channel_id;
/* Remove old channel id from channel lookup */
if (!N_IsChannelIdNil(old_channel_id))
{
u64 bin_index = client->channel_hash % store->num_client_lookup_bins;
PP_ClientLookupBin *bin = &store->client_lookup_bins[bin_index];
PP_Client *prev = PP_ClientFromKey(store, client->prev_in_bin);
PP_Client *next = PP_ClientFromKey(store, client->next_in_bin);
if (prev->valid)
{
prev->next_in_bin = next->key;
}
else
{
bin->first = next->key;
}
if (next->valid)
{
next->prev_in_bin = prev->key;
}
else
{
bin->last = prev->key;
}
}
/* Insert into channel lookup */
/* TODO: Enforce no duplicates */
u64 channel_hash = PP_ClientChannelHashFromChannelId(channel_id);
client->channel_id = channel_id;
client->channel_hash = channel_hash;
if (!N_IsChannelIdNil(channel_id))
{
u64 bin_index = channel_hash % store->num_client_lookup_bins;
PP_ClientLookupBin *bin = &store->client_lookup_bins[bin_index];
{
PP_Client *prev_in_bin = PP_ClientFromKey(store, bin->last);
if (prev_in_bin->valid)
{
prev_in_bin->next_in_bin = client->key;
client->prev_in_bin = prev_in_bin->key;
}
else
{
bin->first = client->key;
}
bin->last = client->key;
}
}
}
PP_Client *PP_ClientFromChannelId(PP_ClientStore *store, N_ChannelId channel_id)
{
PP_Client *result = PP_NilClient();
u64 channel_hash = PP_ClientChannelHashFromChannelId(channel_id);
u64 bin_index = channel_hash % store->num_client_lookup_bins;
PP_ClientLookupBin *bin = &store->client_lookup_bins[bin_index];
for (PP_Client *client = PP_ClientFromKey(store, bin->first); client->valid; client = PP_ClientFromKey(store, client->next_in_bin))
{
if (client->channel_hash == channel_hash)
{
result = client;
break;
}
}
return result;
}
PP_Client *PP_ClientFromKey(PP_ClientStore *store, PP_ClientKey key)
{
if (key.gen != 0 && key.idx < store->num_clients_reserved)
{
PP_Client *client = &store->clients[key.idx];
if (client->key.gen == key.gen)
{
return client;
}
}
return PP_NilClient();
}
////////////////////////////////////////////////////////////
//~ Acquire snapshot
/* Produces a new snapshot at `tick` with data copied from `src` snapshot. */
PP_Snapshot *PP_AcquireSnapshot(PP_Client *client, PP_Snapshot *src, u64 tick)
{
if (tick == 0)
{
return PP_NilSnapshot();
}
PP_Snapshot *ss;
{
Arena *arena = ZI;
Arena *ents_arena = 0;
{
ss = client->first_free_snapshot;
if (ss)
{
/* Re-use existing snasphot arenas */
client->first_free_snapshot = ss->next_free;
ents_arena = ss->ents_arena;
arena = ss->arena;
}
else
{
/* Arenas allocated here will be released with client */
arena = AcquireArena(Gibi(1));
ents_arena = AcquireArena(Gibi(1));
}
}
ResetArena(arena);
ss = PushStruct(arena, PP_Snapshot);
ss->arena = arena;
ss->ents_arena = ents_arena;
ResetArena(ss->ents_arena);
}
ss->tick = tick;
ss->valid = 1;
ss->client = client;
++client->num_ticks;
/* Copy src info */
ss->sim_dt_ns = src->sim_dt_ns;
ss->sim_time_ns = src->sim_time_ns;
ss->continuity_gen = src->continuity_gen;
ss->local_player = src->local_player;
ss->phys_iteration = src->phys_iteration;
/* Copy key lookup bins */
ss->num_key_bins = src->num_key_bins > 0 ? src->num_key_bins : PP_KeyLookupBinsCount;
ss->key_bins = PushStructsNoZero(ss->arena, PP_EntBin, ss->num_key_bins);
if (src->num_key_bins > 0)
{
for (u64 i = 0; i < src->num_key_bins; ++i)
{
ss->key_bins[i] = src->key_bins[i];
}
}
else
{
ZeroBytes(ss->key_bins, sizeof(*ss->key_bins) * ss->num_key_bins);
}
/* Copy entities */
ss->first_free_ent = src->first_free_ent;
ss->num_ents_allocated = src->num_ents_allocated;
ss->num_ents_reserved = src->num_ents_reserved;
ss->ents = PushStructsNoZero(ss->ents_arena, PP_Ent, ss->num_ents_reserved);
if (ss->num_ents_reserved == 0)
{
/* Copying from nil snapshot, need to create blank & root entity */
/* Push blank ent at index 0 (because index 0 is never valid anyway since it maps to nil ent) */
{
PushStruct(ss->ents_arena, PP_Ent);
++ss->num_ents_allocated;
++ss->num_ents_reserved;
}
/* Push root ent with constant key */
{
PP_Ent *root = PushStructNoZero(ss->ents_arena, PP_Ent);
*root = *PP_NilEnt();
root->ss = ss;
root->valid = 1;
root->is_root = 1;
root->mass_unscaled = F32Infinity;
root->inertia_unscaled = F32Infinity;
PP_SetEntKey(root, PP_RootEntKey);
++ss->num_ents_allocated;
++ss->num_ents_reserved;
}
}
else
{
for (u64 i = 0; i < ss->num_ents_reserved; ++i)
{
PP_Ent *dst_ent = &ss->ents[i];
PP_Ent *src_ent = &src->ents[i];
*dst_ent = *src_ent;
dst_ent->ss = ss;
}
}
/* Release duplicate tick if it exists */
{
PP_Snapshot *existing = PP_SnapshotFromTick(client, tick);
if (existing->valid)
{
PP_ReleaseSnapshot(existing);
}
}
/* Linear search to insert snapshot in tick order */
{
PP_Snapshot *prev = PP_SnapshotFromTick(client, client->last_tick);
while (prev->valid)
{
if (prev->tick < tick)
{
break;
}
prev = PP_SnapshotFromTick(client, prev->prev_tick);
}
if (prev->valid)
{
PP_Snapshot *next = PP_SnapshotFromTick(client, prev->next_tick);
if (next->valid)
{
next->prev_tick = tick;
}
else
{
client->last_tick = tick;
}
ss->next_tick = prev->next_tick;
ss->prev_tick = prev->tick;
prev->next_tick = ss->tick;
}
else
{
PP_Snapshot *first = PP_SnapshotFromTick(client, client->first_tick);
if (first->valid)
{
ss->next_tick = first->tick;
first->prev_tick = tick;
}
else
{
client->last_tick = tick;
}
ss->next_tick = client->first_tick;
client->first_tick = tick;
}
}
/* Insert into lookup */
{
u64 bin_index = tick % client->num_snapshot_lookup_bins;
PP_SnapshotLookupBin *bin = &client->snapshot_lookup_bins[bin_index];
if (bin->last)
{
bin->last->next_in_bin = ss;
ss->prev_in_bin = bin->last;
}
else
{
bin->first = ss;
}
bin->last = ss;
}
return ss;
}
void PP_ReleaseSnapshot(PP_Snapshot *ss)
{
PP_Client *client = ss->client;
/* Remove from lookup */
{
u64 bin_index = ss->tick % client->num_snapshot_lookup_bins;
PP_SnapshotLookupBin *bin = &client->snapshot_lookup_bins[bin_index];
PP_Snapshot *prev = ss->prev_in_bin;
PP_Snapshot *next = ss->next_in_bin;
if (prev)
{
prev->next_in_bin = next;
}
else
{
bin->first = next;
}
if (next)
{
next->prev_in_bin = prev;
}
else
{
bin->last = prev;
}
}
/* Remove from snapshot list */
{
PP_Snapshot *prev = PP_SnapshotFromTick(client, ss->prev_tick);
PP_Snapshot *next = PP_SnapshotFromTick(client, ss->next_tick);
if (prev->valid)
{
prev->next_tick = next->tick;
}
else
{
client->first_tick = next->tick;
}
if (next->valid)
{
next->prev_tick = prev->tick;
}
else
{
client->last_tick = prev->tick;
}
}
ss->valid = 0;
ss->next_free = client->first_free_snapshot;
client->first_free_snapshot = ss;
--client->num_ticks;
}
/* Release all snapshots for client with tick in range [start, end] */
void PP_ReleaseSnapshotsInRange(PP_Client *client, u64 start, u64 end)
{
if (start > end)
{
u64 swp = start;
start = end;
end = swp;
}
PP_Snapshot *ss = PP_SnapshotFromTick(client, client->first_tick);
while (ss->valid)
{
u64 tick = ss->tick;
u64 next_tick = ss->next_tick;
if (tick >= start)
{
if (tick <= end)
{
PP_ReleaseSnapshot(ss);
}
else
{
break;
}
}
ss = PP_SnapshotFromTick(client, next_tick);
}
}
////////////////////////////////////////////////////////////
//~ Snapshot lookup
PP_Snapshot *PP_SnapshotFromTick(PP_Client *client, u64 tick)
{
PP_Snapshot *ss = PP_NilSnapshot();
if (tick > 0)
{
u64 bin_index = tick % client->num_snapshot_lookup_bins;
PP_SnapshotLookupBin *bin = &client->snapshot_lookup_bins[bin_index];
for (PP_Snapshot *search = bin->first; search; search = search->next_in_bin)
{
if (search->tick == tick)
{
ss = search;
break;
}
}
}
return ss;
}
/* Returns the snapshot at nearest valid tick <= supplied tick */
PP_Snapshot *PP_SnapshotFromClosestTickLte(PP_Client *client, u64 tick)
{
PP_Snapshot *ss = PP_SnapshotFromTick(client, tick);
if (!ss->valid)
{
/* Degenerate to linear search */
ss = PP_SnapshotFromTick(client, client->last_tick);
while (ss->valid)
{
if (ss->tick <= tick)
{
break;
}
ss = PP_SnapshotFromTick(client, ss->prev_tick);
}
}
return ss;
}
/* Returns the snapshot at nearest valid tick >= supplied tick */
PP_Snapshot *PP_SnapshotFromClosestTickGte(PP_Client *client, u64 tick)
{
PP_Snapshot *ss = PP_SnapshotFromTick(client, tick);
if (!ss->valid)
{
/* Degenerate to linear search */
ss = PP_SnapshotFromTick(client, client->first_tick);
while (ss->valid)
{
if (ss->tick >= tick)
{
break;
}
ss = PP_SnapshotFromTick(client, ss->next_tick);
}
}
return ss;
}
////////////////////////////////////////////////////////////
//~ Tile
Vec2I32 PP_WorldTileIndexFromPos(Vec2 pos)
{
Vec2I32 result = VEC2I32(pos.x * SIM_TILES_PER_UNIT_SQRT, pos.y * SIM_TILES_PER_UNIT_SQRT);
result.x -= pos.x < 0;
result.y -= pos.y < 0;
return result;
}
Vec2 PP_PosFromWorldTileIndex(Vec2I32 world_tile_index)
{
Vec2 result = ZI;
f32 tile_size = 1.f / SIM_TILES_PER_UNIT_SQRT;
result.x = (f32)world_tile_index.x * tile_size;
result.y = (f32)world_tile_index.y * tile_size;
return result;
}
Vec2I32 PP_LocalTileIndexFromWorldTileIndex(Vec2I32 world_tile_index)
{
Vec2I32 result = world_tile_index;
result.x += result.x < 0;
result.y += result.y < 0;
result.x = result.x % SIM_TILES_PER_CHUNK_SQRT;
result.y = result.y % SIM_TILES_PER_CHUNK_SQRT;
result.x += (world_tile_index.x < 0) * (SIM_TILES_PER_CHUNK_SQRT - 1);
result.y += (world_tile_index.y < 0) * (SIM_TILES_PER_CHUNK_SQRT - 1);
return result;
}
Vec2I32 PP_WorldTileIndexFromLocalTileIndex(Vec2I32 tile_chunk_index, Vec2I32 local_tile_index)
{
Vec2I32 result = ZI;
result.x = (tile_chunk_index.x * SIM_TILES_PER_CHUNK_SQRT) + local_tile_index.x;
result.y = (tile_chunk_index.y * SIM_TILES_PER_CHUNK_SQRT) + local_tile_index.y;
return result;
}
Vec2I32 PP_TileChunkIndexFromWorldTileIndex(Vec2I32 world_tile_index)
{
Vec2I32 result = world_tile_index;
result.x += result.x < 0;
result.y += result.y < 0;
result.x = result.x / SIM_TILES_PER_CHUNK_SQRT;
result.y = result.y / SIM_TILES_PER_CHUNK_SQRT;
result.x -= world_tile_index.x < 0;
result.y -= world_tile_index.y < 0;
return result;
}
void PP_SetSnapshotTile(PP_Snapshot *ss, Vec2I32 world_tile_index, PP_TileKind tile_kind)
{
Vec2I32 chunk_index = PP_TileChunkIndexFromWorldTileIndex(world_tile_index);
PP_EntKey chunk_id = PP_TileChunkKeyFromIndex(chunk_index);
PP_Ent *chunk_ent = PP_EntFromKey(ss, chunk_id);
if (!chunk_ent->valid)
{
PP_Ent *root = PP_EntFromKey(ss, PP_RootEntKey);
chunk_ent = PP_AcquireSyncSrcEntWithKey(root, chunk_id);
PP_EnableProp(chunk_ent, PP_Prop_TileChunk);
chunk_ent->tile_chunk_index = chunk_index;
}
Vec2I32 local_index = PP_LocalTileIndexFromWorldTileIndex(world_tile_index);
chunk_ent->tile_chunk_tiles[local_index.x + (local_index.y * SIM_TILES_PER_CHUNK_SQRT)] = tile_kind;
}
////////////////////////////////////////////////////////////
//~ Snapshot lerp
PP_Snapshot *PP_AcquireSnapshotFromLerp(PP_Client *client, PP_Snapshot *ss0, PP_Snapshot *ss1, f64 blend)
{
__prof;
/* New snapshot will be allocated with same tick as ss0 or ss1, so the result should go into a different client */
Assert(ss0->client != client && ss1->client != client);
PP_Snapshot *ss;
b32 should_blend = 1;
if (ss0->continuity_gen == ss1->continuity_gen && 0 < blend && blend < 1)
{
ss = PP_AcquireSnapshot(client, ss0, ss0->tick);
}
else if (RoundF64ToI64(blend) <= 0)
{
ss = PP_AcquireSnapshot(client, ss0, ss0->tick);
should_blend = 0;
}
else
{
ss = PP_AcquireSnapshot(client, ss1, ss1->tick);
should_blend = 0;
}
if (!ss0->valid || !ss1->valid)
{
// New snapshot allocation caused one of the src snapshots original to release.
// ss0 & ss1 should be from a separate client than the allocating one.
Assert(0);
}
if (should_blend)
{
/* Blend time */
ss->sim_dt_ns = LerpI64(ss0->sim_dt_ns, ss1->sim_dt_ns, blend);
ss->sim_time_ns = LerpI64(ss0->sim_time_ns, ss1->sim_time_ns, blend);
/* Blend entities */
{
__profn("Lerp snapshot entities");
u64 num_entities = MinU64(ss0->num_ents_reserved, ss1->num_ents_reserved);
for (u64 i = 0; i < num_entities; ++i)
{
PP_Ent *e = &ss->ents[i];
PP_Ent *e0 = &ss0->ents[i];
PP_Ent *e1 = &ss1->ents[i];
PP_LerpEnt(e, e0, e1, blend);
}
}
}
return ss;
}
////////////////////////////////////////////////////////////
//~ Snapshot sync
/* Syncs entity data between snapshots */
void PP_SyncSnapshotEnts(PP_Snapshot *local_ss, PP_Snapshot *remote_ss, PP_EntKey remote_player, u32 sync_flags)
{
__prof;
// FIXME: Don't trust non-master clients:
// - Only sync cmd ents
// - Determine new UUids for newly created ents
PP_Ent *local_root = PP_EntFromKey(local_ss, PP_RootEntKey);
PP_Ent *remote_root = PP_EntFromKey(remote_ss, PP_RootEntKey);
/* Create new ents from remote */
for (PP_Ent *remote_top = PP_EntFromKey(remote_ss, remote_root->first); remote_top->valid; remote_top = PP_EntFromKey(remote_ss, remote_top->next))
{
PP_CreateMissingEntsFromSnapshots(local_root, remote_top, remote_player);
}
/* Sync ents with remote, skipping index 0 (nil) & index 1 (root) */
for (u64 i = 2; i < local_ss->num_ents_reserved; ++i)
{
PP_Ent *local_ent = &local_ss->ents[i];
if (local_ent->valid && PP_HasProp(local_ent, PP_Prop_SyncDst))
{
b32 should_sync = PP_EqEntKey(local_ent->owner, remote_player) || PP_IsNilEntKey(remote_player);
if ((sync_flags & PP_SyncFlag_NoSyncPredictables) && PP_EqEntKey(local_ent->predictor, local_ss->local_player))
{
should_sync = 0;
}
if (should_sync)
{
PP_Ent *remote_ent = PP_EntFromKey(remote_ss, local_ent->key);
if (remote_ent->valid)
{
/* Copy all ent data from remote */
PP_SyncEnt(local_ent, remote_ent);
}
else
{
/* Remote ent is no longer valid / networked, release it */
PP_EnableProp(local_ent, PP_Prop_Release);
PP_DisableProp(local_ent, PP_Prop_SyncDst);
}
}
}
}
PP_ReleaseAllWithProp(local_ss, PP_Prop_Release);
}
////////////////////////////////////////////////////////////
//~ Snapshot encode
void PP_EncodeSnapshot(BB_Writer *bw, PP_Client *receiver, PP_Snapshot *ss0, PP_Snapshot *ss1)
{
__prof;
BB_WriteDebugMarker(bw, Lit("SNAPSHOT START"));
BB_WriteIV(bw, ss1->sim_dt_ns);
BB_WriteIV(bw, ss1->sim_time_ns);
BB_WriteUV(bw, ss1->continuity_gen);
BB_WriteUV(bw, ss1->phys_iteration);
BB_WriteUid(bw, receiver->player_id.uid);
/* Key bins */
/* TODO: Don't encode these */
BB_WriteDebugMarker(bw, Lit("SNAPSHOT BINS"));
for (u64 i = 0; i < ss1->num_key_bins; ++i)
{
u32 old_first = 0;
u32 old_last = 0;
if (i < ss0->num_key_bins)
{
PP_EntBin *old_bin = &ss0->key_bins[i];
old_first = old_bin->first;
old_last = old_bin->last;
}
PP_EntBin *bin = &ss1->key_bins[i];
b32 first_diff = bin->first != old_first;
b32 last_diff = bin->last != old_last;
if (first_diff || last_diff)
{
BB_WriteBit(bw, 1);
BB_WriteUV(bw, i);
if (BB_WriteBit(bw, first_diff))
{
BB_WriteUV(bw, bin->first);
}
if (BB_WriteBit(bw, last_diff))
{
BB_WriteUV(bw, bin->last);
}
}
}
BB_WriteBit(bw, 0);
/* Ents */
BB_WriteDebugMarker(bw, Lit("SNAPSHOT NUM ENTS"));
if (BB_WriteBit(bw, ss1->num_ents_allocated != ss0->num_ents_allocated))
{
BB_WriteUV(bw, ss1->num_ents_allocated);
}
if (BB_WriteBit(bw, ss1->num_ents_reserved != ss0->num_ents_reserved))
{
BB_WriteUV(bw, ss1->num_ents_reserved);
}
BB_WriteDebugMarker(bw, Lit("SNAPSHOT ENTS"));
BB_WriteDebugMarker(bw, StringFromStruct(&ss1->num_ents_reserved));
for (u64 i = 1; i < ss1->num_ents_reserved; ++i)
{
PP_Ent *e0 = PP_NilEnt();
if (i < ss0->num_ents_reserved)
{
e0 = &ss0->ents[i];
}
PP_Ent *e1 = &ss1->ents[i];
PP_EncodeEnt(bw, e0, e1);
}
BB_WriteDebugMarker(bw, Lit("SNAPSHOT END"));
}
////////////////////////////////////////////////////////////
//~ Snapshot decode
void PP_DecodeSnapshot(BB_Reader *br, PP_Snapshot *ss)
{
__prof;
BB_ReadDebugMarker(br, Lit("SNAPSHOT START"));
ss->sim_dt_ns = BB_ReadIV(br);
ss->sim_time_ns = BB_ReadIV(br);
ss->continuity_gen = BB_ReadUV(br);
ss->phys_iteration = BB_ReadUV(br);
ss->local_player = (PP_EntKey){ .uid = BB_ReadUid(br) };
/* Key bins */
/* TODO: Don't decode these, determine them implicitly from decoded ents */
BB_ReadDebugMarker(br, Lit("SNAPSHOT BINS"));
{
b32 bin_changed = BB_ReadBit(br);
while (bin_changed)
{
u32 bin_index = BB_ReadUV(br);
if (bin_index < ss->num_key_bins)
{
PP_EntBin *bin = &ss->key_bins[bin_index];
if (BB_ReadBit(br))
{
bin->first = BB_ReadUV(br);
}
if (BB_ReadBit(br))
{
bin->last = BB_ReadUV(br);
}
}
else
{
/* Invalid bin index */
Assert(0);
}
bin_changed = BB_ReadBit(br);
}
}
/* Ents */
BB_ReadDebugMarker(br, Lit("SNAPSHOT NUM ENTS"));
if (BB_ReadBit(br))
{
ss->num_ents_allocated = BB_ReadUV(br);
}
b32 num_ents_reserved_changed = BB_ReadBit(br);
if (num_ents_reserved_changed)
{
u64 old_num_ents_reserved = ss->num_ents_reserved;
ss->num_ents_reserved = BB_ReadUV(br);
i64 reserve_diff = (i64)ss->num_ents_reserved - (i64)old_num_ents_reserved;
if (reserve_diff > 0)
{
PushStructsNoZero(ss->ents_arena, PP_Ent, reserve_diff);
for (u64 i = old_num_ents_reserved; i < ss->num_ents_reserved; ++i)
{
PP_Ent *e = &ss->ents[i];
*e = *PP_NilEnt();
e->ss = ss;
}
}
else if (reserve_diff < 0)
{
/* TODO: Handle this */
/* NOTE: Should be impossible for snasphot reserve count to decrease at the moment */
Assert(0);
}
}
BB_ReadDebugMarker(br, Lit("SNAPSHOT ENTS"));
BB_ReadDebugMarker(br, StringFromStruct(&ss->num_ents_reserved));
for (u64 i = 1; i < ss->num_ents_reserved; ++i)
{
PP_Ent *e = &ss->ents[i];
e->ss = ss;
PP_DecodeEnt(br, e);
}
BB_ReadDebugMarker(br, Lit("SNAPSHOT END"));
}

View File

@ -1,322 +0,0 @@
////////////////////////////////////////////////////////////
//~ Key types
Struct(PP_EntKey)
{
Uid uid;
};
Struct(PP_ClientKey)
{
u32 idx;
u32 gen;
};
#define PP_NilClientKey ((PP_ClientKey) { .gen = 0, .idx = 0 })
////////////////////////////////////////////////////////////
//~ Client store types
Struct(PP_ClientLookupBin)
{
PP_ClientKey first;
PP_ClientKey last;
};
Struct(PP_ClientStore)
{
b32 valid;
Arena *arena;
/* Client lookup */
PP_ClientLookupBin *client_lookup_bins;
u64 num_client_lookup_bins;
/* Clients */
Arena *clients_arena;
struct PP_Client *clients;
PP_ClientKey first_free_client;
u64 num_clients_allocated;
u64 num_clients_reserved;
};
Inline PP_ClientStore *PP_NilClientStore(void)
{
extern Readonly PP_ClientStore **PP_nil_client_store;
return *PP_nil_client_store;
}
////////////////////////////////////////////////////////////
//~ Client types
Struct(PP_SnapshotLookupBin)
{
struct PP_Snapshot *first;
struct PP_Snapshot *last;
};
Struct(PP_Client)
{
b32 valid;
PP_ClientKey key;
PP_ClientStore *store;
Arena *snapshots_arena;
/* Round trip time of the client (if networked) */
i64 last_rtt_ns;
N_ChannelId channel_id;
u64 channel_hash;
PP_ClientKey next_free;
PP_ClientKey next_in_bin;
PP_ClientKey prev_in_bin;
/* The client's player entity key in the master sim (if relevant) */
PP_EntKey player_id;
/* This is the highest confirmed tick of ours that we know this client has received */
u64 ack;
// This is the highest confirmed ack of ours that we know this client has received (this
// can be used to determine which client ticks will no longer be delta encoded from and
// therefore can be released)
u64 double_ack;
/* This is the highest tick of their's that we have received */
u64 highest_received_tick;
/* Snapshots sorted by tick (low to high) */
u64 first_tick;
u64 last_tick;
u64 num_ticks;
struct PP_Snapshot *first_free_snapshot;
/* Tick -> snapshot lookup */
u64 num_snapshot_lookup_bins;
PP_SnapshotLookupBin *snapshot_lookup_bins;
};
Inline PP_Client *PP_NilClient(void)
{
extern Readonly PP_Client **PP_nil_client;
return *PP_nil_client;
}
Inline b32 PP_EqClientKey(PP_ClientKey a, PP_ClientKey b)
{
return a.gen == b.gen && a.idx == b.idx;
}
////////////////////////////////////////////////////////////
//~ Layer types
/* Absolute layers */
#define PP_Layer_FloorDecals (-300)
#define PP_Layer_Bullets (-200)
#define PP_Layer_Tracers (-100)
#define PP_Layer_Shoulders (0)
#define PP_Layer_Walls (100)
/* Relative layers */
#define PP_Layer_DefaultRelative (0)
#define PP_Layer_RelativeWeapon (1)
////////////////////////////////////////////////////////////
//~ Control types
Enum(PP_ControlFlag)
{
PP_ControlFlag_Fire = 1 << 0,
PP_ControlFlag_AltFire = 1 << 1,
/* Testing */
PP_ControlFlag_Drag = 1 << 2,
PP_ControlFlag_Delete = 1 << 3,
PP_ControlFlag_ClearAll = 1 << 4,
PP_ControlFlag_SpawnTest1 = 1 << 5,
PP_ControlFlag_SpawnTest2 = 1 << 6,
PP_ControlFlag_SpawnTest3 = 1 << 7,
PP_ControlFlag_SpawnTest4 = 1 << 8,
PP_ControlFlag_TestWalls = 1 << 9,
PP_ControlFlag_TestTiles = 1 << 10,
PP_ControlFlag_TestExplode = 1 << 11,
PP_ControlFlag_TestTeleport = 1 << 12,
};
Struct(PP_ControlData)
{
Vec2 move; /* Movement direction vector (speed of 0 -> 1) */
Vec2 focus; /* Focus direction vector (where does the controller want to look) */
Vec2 dbg_cursor; /* Where is the user's cursor in the world (used for things like editing the world) */
PP_ControlFlag flags;
};
Enum(PP_CmdKind)
{
PP_CmdKind_Invalid,
PP_CmdKind_Control,
PP_CmdKind_Chat
};
////////////////////////////////////////////////////////////
//~ Tile types
Enum(PP_TileKind)
{
PP_TileKind_None,
PP_TileKind_Wall,
PP_TileKind_COUNT
};
StaticAssert(PP_TileKind_COUNT < 256); /* Tile kind must fit in 8 bits */
////////////////////////////////////////////////////////////
//~ Snapshot types
Enum(PP_SyncFlag)
{
PP_SyncFlag_NoSyncPredictables = 1 << 0
};
Struct(PP_Snapshot)
{
b32 valid;
u64 tick;
PP_Client *client;
PP_Snapshot *next_free;
PP_Snapshot *next_in_bin;
PP_Snapshot *prev_in_bin;
u64 prev_tick;
u64 next_tick;
Arena *arena;
/* Sim time (guaranteed to increase by sim_dt_ns each step) */
i64 sim_dt_ns;
i64 sim_time_ns;
/* If != previous tick's continuity then don't lerp */
u64 continuity_gen;
/* The last physics iteration (used for tracking contact lifetime) */
u64 phys_iteration;
/* The key of the receiver's player in the snapshot */
PP_EntKey local_player;
/* Key lookup */
struct PP_EntBin *key_bins;
u64 num_key_bins;
/* Ents */
Arena *ents_arena;
struct PP_Ent *ents;
u32 first_free_ent;
u32 num_ents_allocated;
u32 num_ents_reserved;
};
Inline PP_Snapshot *PP_NilSnapshot(void)
{
extern Readonly PP_Snapshot **PP_nil_snapshot;
return *PP_nil_snapshot;
}
////////////////////////////////////////////////////////////
//~ State types
#define PP_ClientLookupBinsCount 127
#define PP_TickLookupBinsCount 127
#define PP_KeyLookupBinsCount 4096
Struct(PP_SharedSimCtx)
{
Arena *nil_arena;
PP_ClientStore *nil_client_store;
PP_Client *nil_client;
PP_Snapshot *nil_snapshot;
struct PP_Ent *nil_ent;
} extern PP_shared_sim_ctx;
/* Accessed via `PP_NilClientStore()` */
extern Readonly PP_ClientStore **PP_nil_client_store;
/* Accessed via `PP_NilClient()` */
extern Readonly PP_Client **PP_nil_client;
/* Accessed via `NilSnapshot()` */
extern Readonly PP_Snapshot **PP_nil_snapshot;
extern Readonly struct PP_Ent **PP_nil_ent;
////////////////////////////////////////////////////////////
//~ Startup
void PP_StartupSim(void);
////////////////////////////////////////////////////////////
//~ Client store acquire operations
PP_ClientStore *PP_AcquireClientStore(void);
void PP_ReleaseClientStore(PP_ClientStore *store);
////////////////////////////////////////////////////////////
//~ Client acquire operations
PP_Client *PP_AcquireClient(PP_ClientStore *store);
void PP_ReleaseClient(PP_Client *client);
////////////////////////////////////////////////////////////
//~ Client lookup operations
u64 PP_ClientChannelHashFromChannelId(N_ChannelId channel_id);
void PP_SetClientChannelId(PP_Client *client, N_ChannelId channel_id);
PP_Client *PP_ClientFromChannelId(PP_ClientStore *store, N_ChannelId channel_id);
PP_Client *PP_ClientFromKey(PP_ClientStore *store, PP_ClientKey key);
////////////////////////////////////////////////////////////
//~ Snapshot acquire operations
PP_Snapshot *PP_AcquireSnapshot(PP_Client *client, PP_Snapshot *src, u64 tick);
void PP_ReleaseSnapshot(PP_Snapshot *ss);
void PP_ReleaseSnapshotsInRange(PP_Client *client, u64 start, u64 end);
////////////////////////////////////////////////////////////
//~ Snapshot lookup operations
PP_Snapshot *PP_SnapshotFromTick(PP_Client *client, u64 tick);
PP_Snapshot *PP_SnapshotFromClosestTickLte(PP_Client *client, u64 tick);
PP_Snapshot *PP_SnapshotFromClosestTickGte(PP_Client *client, u64 tick);
////////////////////////////////////////////////////////////
//~ Tile operations
Vec2I32 PP_WorldTileIndexFromPos(Vec2 pos);
Vec2 PP_PosFromWorldTileIndex(Vec2I32 world_tile_index);
Vec2I32 PP_LocalTileIndexFromWorldTileIndex(Vec2I32 world_tile_index);
Vec2I32 PP_WorldTileIndexFromLocalTileIndex(Vec2I32 tile_chunk_index, Vec2I32 local_tile_index);
Vec2I32 PP_TileChunkIndexFromWorldTileIndex(Vec2I32 world_tile_index);
void PP_SetSnapshotTile(PP_Snapshot *ss, Vec2I32 world_tile_index, PP_TileKind tile_kind);
////////////////////////////////////////////////////////////
//~ Snapshot lerp operations
PP_Snapshot *PP_AcquireSnapshotFromLerp(PP_Client *client, PP_Snapshot *ss0, PP_Snapshot *ss1, f64 blend);
////////////////////////////////////////////////////////////
//~ Snapshot sync operations
void PP_SyncSnapshotEnts(PP_Snapshot *local_ss, PP_Snapshot *remote_ss, PP_EntKey remote_player, u32 sync_flags);
////////////////////////////////////////////////////////////
//~ Snapshot encode operations
void PP_EncodeSnapshot(BB_Writer *bw, PP_Client *receiver, PP_Snapshot *ss0, PP_Snapshot *ss1);
////////////////////////////////////////////////////////////
//~ Snapshot decode operations
void PP_DecodeSnapshot(BB_Reader *br, PP_Snapshot *ss);

View File

@ -1,493 +0,0 @@
/* FIXME: Default space entry & cell pointers to nil */
Readonly PP_SpaceEntry PP_nil_space_entry = ZI;
Readonly PP_SpaceCell PP_nil_space_cell = ZI;
Readonly PP_Space PP_nil_space = ZI;
////////////////////////////////////////////////////////////
//~ Space
// NOTE:
// The number of bins determines how often tiles will collide in the spatial hash.
// For example, at `num_bins_sqrt` = 256 (65536 bins), tiles <1, 1>, <1, 257>, and <257, 257> will collide. */
PP_Space *PP_AcquireSpace(f32 cell_size, u32 num_bins_sqrt)
{
PP_Space *space;
{
Arena *arena = AcquireArena(Gibi(64));
space = PushStruct(arena, PP_Space);
space->entry_arena = arena;
}
space->valid = 1;
space->entries = PushDry(space->entry_arena, PP_SpaceEntry);
space->cell_arena = AcquireArena(Gibi(64));
space->cell_size = cell_size;
space->num_bins = num_bins_sqrt * num_bins_sqrt;
space->num_bins_sqrt = num_bins_sqrt;
space->bins = PushStructs(space->cell_arena, PP_SpaceCellBin, space->num_bins);
return space;
}
void PP_ReleaseSpace(PP_Space *space)
{
ReleaseArena(space->cell_arena);
ReleaseArena(space->entry_arena);
}
void PP_ResetSpace(PP_Space *space)
{
PopTo(space->entry_arena, (u64)space->entries - (u64)ArenaBase(space->entry_arena));
ResetArena(space->cell_arena);
space->bins = PushStructs(space->cell_arena, PP_SpaceCellBin, space->num_bins);
space->num_entries_reserved = 0;
space->first_free_cell = 0;
space->first_free_cell_node = 0;
space->first_free_entry = 0;
}
PP_Space *PP_SpaceFromEntry(PP_SpaceEntry *entry)
{
if (entry->valid)
{
u64 first_entry_addr = (u64)(entry - entry->key.idx);
PP_Space *space = (PP_Space *)(first_entry_addr - PP_SpaceEntriesOffset);
Assert(space->entries == (PP_SpaceEntry *)first_entry_addr);
return space;
}
else
{
return PP_NilSpace();
}
}
////////////////////////////////////////////////////////////
//~ Cell
Vec2I32 PP_SpaceCellCoordsFromWorldCoords(f32 cell_size, Vec2 world_pos)
{
f32 x = world_pos.x;
f32 y = world_pos.y;
x = (x + ((x >= 0) - (x < 0)) * cell_size) / cell_size;
y = (y + ((y >= 0) - (y < 0)) * cell_size) / cell_size;
return VEC2I32((i32)x, (i32)y);
}
i32 PP_SpaceBinIndexFromCellCoords(PP_Space *space, Vec2I32 cell_pos)
{
i32 num_bins_sqrt = space->num_bins_sqrt;
/* Cell pos of 0 is not valid and will be converted to -1 */
Assert(cell_pos.x != 0 && cell_pos.y != 0);
i32 index_x = cell_pos.x;
i32 index_y = cell_pos.y;
/* Offset cell index by -1 since cell pos of 0 is invalid */
index_x -= (index_x >= 0);
index_y -= (index_y >= 0);
/* Un-mirror coords to prevent collisions between cells near the axes. (e.g. <3, 1> & <3, -1> should not collide) */
index_x += (index_x < 0) * (num_bins_sqrt * ((index_x / -num_bins_sqrt) + 1));
index_y += (index_y < 0) * (num_bins_sqrt * ((index_y / -num_bins_sqrt) + 1));
i32 bin_index = (index_x % num_bins_sqrt) + (index_y % num_bins_sqrt) * num_bins_sqrt;
Assert(bin_index >= 0 && bin_index < (i32)space->num_bins);
return bin_index;
}
PP_SpaceCell *PP_SpaceCellFromCellPos(PP_Space *space, Vec2I32 cell_pos)
{
i32 bin_index = PP_SpaceBinIndexFromCellCoords(space, cell_pos);
PP_SpaceCellBin *bin = &space->bins[bin_index];
PP_SpaceCell *result = PP_NilSpaceCell();
for (PP_SpaceCell *n = bin->first_cell; n; n = n->next_in_bin)
{
if (EqVec2I32(n->pos, cell_pos))
{
result = n;
break;
}
}
return result;
}
void PP_AcquireSpaceCellNode(Vec2I32 cell_pos, PP_SpaceEntry *entry)
{
PP_Space *space = PP_SpaceFromEntry(entry);
i32 bin_index = PP_SpaceBinIndexFromCellCoords(space, cell_pos);
PP_SpaceCellBin *bin = &space->bins[bin_index];
/* Find existing cell */
PP_SpaceCell *cell = 0;
for (PP_SpaceCell *n = bin->first_cell; n; n = n->next_in_bin)
{
if (EqVec2I32(n->pos, cell_pos))
{
cell = n;
break;
}
}
/* Acquire new cell if necessary */
if (!cell)
{
if (space->first_free_cell)
{
cell = space->first_free_cell;
space->first_free_cell = cell->next_free;
}
else
{
cell = PushStructNoZero(space->cell_arena, PP_SpaceCell);
}
ZeroStruct(cell);
if (bin->last_cell)
{
bin->last_cell->next_in_bin = cell;
cell->prev_in_bin = bin->last_cell;
}
else
{
bin->first_cell = cell;
}
bin->last_cell = cell;
cell->pos = cell_pos;
cell->bin = bin;
cell->valid = 1;
}
/* Acquire node */
PP_SpaceCellNode *node;
{
if (space->first_free_cell_node)
{
node = space->first_free_cell_node;
space->first_free_cell_node = node->next_free;
}
else
{
node = PushStructNoZero(space->cell_arena, PP_SpaceCellNode);
}
ZeroStruct(node);
}
/* Insert into cell list */
node->cell = cell;
if (cell->last_node)
{
cell->last_node->next_in_cell = node;
node->prev_in_cell = cell->last_node;
}
else
{
cell->first_node = node;
}
cell->last_node = node;
/* Insert into entry list */
node->entry = entry;
if (entry->last_node)
{
entry->last_node->next_in_entry = node;
node->prev_in_entry = entry->last_node;
}
else
{
entry->first_node = node;
}
entry->last_node = node;
}
void PP_ReleaseSpaceCellNode(PP_SpaceCellNode *n)
{
PP_SpaceCell *cell = n->cell;
PP_SpaceEntry *entry = n->entry;
PP_Space *space = PP_SpaceFromEntry(entry);
PP_SpaceCellBin *bin = cell->bin;
/* Remove from entry list */
{
PP_SpaceCellNode *prev = n->prev_in_entry;
PP_SpaceCellNode *next = n->next_in_entry;
if (prev)
{
prev->next_in_entry = next;
}
else
{
entry->first_node = next;
}
if (next)
{
next->prev_in_entry = prev;
}
else
{
entry->last_node = prev;
}
}
/* Remove from cell list */
{
PP_SpaceCellNode *prev = n->prev_in_cell;
PP_SpaceCellNode *next = n->next_in_cell;
if (prev)
{
prev->next_in_cell = next;
}
else
{
cell->first_node = next;
}
if (next)
{
next->prev_in_cell = prev;
}
else
{
cell->last_node = prev;
}
}
/* If cell is now empty, release it */
if (!cell->first_node && !cell->last_node)
{
/* Remove from bin */
PP_SpaceCell *prev = cell->prev_in_bin;
PP_SpaceCell *next = cell->next_in_bin;
if (prev)
{
prev->next_in_bin = next;
}
else
{
bin->first_cell = next;
}
if (next)
{
next->prev_in_bin = prev;
}
else
{
bin->last_cell = prev;
}
cell->valid = 0;
/* Insert cell into free list */
cell->next_free = space->first_free_cell;
space->first_free_cell = cell;
}
/* Insert node into free list */
n->next_free = space->first_free_cell_node;
space->first_free_cell_node = n;
}
////////////////////////////////////////////////////////////
//~ Entry
PP_SpaceEntry *PP_SpaceEntryFromKey(PP_Space *space, PP_SpaceEntryKey key)
{
PP_SpaceEntry *entry = PP_NilSpaceEntry();
if (key.gen > 0 && key.idx < space->num_entries_reserved)
{
PP_SpaceEntry *tmp = &space->entries[key.idx];
if (tmp->key.gen == key.gen)
{
entry = tmp;
}
}
return entry;
}
PP_SpaceEntry *PP_AcquireSpaceEntry(PP_Space *space, PP_EntKey ent)
{
PP_SpaceEntry *entry = 0;
PP_SpaceEntryKey key = ZI;
if (space->first_free_entry)
{
entry = space->first_free_entry;
space->first_free_entry = entry->next_free;
key = entry->key;
}
else
{
entry = PushStructNoZero(space->entry_arena, PP_SpaceEntry);
key.idx = space->num_entries_reserved;
key.gen = 1;
++space->num_entries_reserved;
}
ZeroStruct(entry);
entry->valid = 1;
entry->key = key;
entry->ent = ent;
return entry;
}
void PP_ReleaseSpaceEntry(PP_SpaceEntry *entry)
{
/* Release nodes */
PP_SpaceCellNode *n = entry->first_node;
while (n)
{
PP_SpaceCellNode *next = n->next_in_entry;
/* TODO: More efficient batch release that doesn't care about maintaining entry list */
PP_ReleaseSpaceCellNode(n);
n = next;
}
PP_Space *space = PP_SpaceFromEntry(entry);
entry->next_free = space->first_free_entry;
entry->valid = 0;
++entry->key.gen;
space->first_free_entry = entry;
}
void PP_UpdateSpaceEntryAabb(PP_SpaceEntry *entry, Aabb new_aabb)
{
PP_Space *space = PP_SpaceFromEntry(entry);
f32 cell_size = space->cell_size;
Vec2I32 old_cell_p0 = VEC2I32(0, 0);
Vec2I32 old_cell_p1 = VEC2I32(0, 0);
if (entry->first_node)
{
Aabb old_aabb = entry->aabb;
old_cell_p0 = PP_SpaceCellCoordsFromWorldCoords(cell_size, old_aabb.p0);
old_cell_p1 = PP_SpaceCellCoordsFromWorldCoords(cell_size, old_aabb.p1);
}
Vec2I32 new_cell_p0 = PP_SpaceCellCoordsFromWorldCoords(cell_size, new_aabb.p0);
Vec2I32 new_cell_p1 = PP_SpaceCellCoordsFromWorldCoords(cell_size, new_aabb.p1);
/* Release outdated nodes */
PP_SpaceCellNode *n = entry->first_node;
while (n)
{
PP_SpaceCell *cell = n->cell;
Vec2I32 cell_pos = cell->pos;
if (cell_pos.x < new_cell_p0.x || cell_pos.x > new_cell_p1.x || cell_pos.y < new_cell_p0.y || cell_pos.y > new_cell_p1.y)
{
/* Cell is outside of new AABB */
PP_SpaceCellNode *next = n->next_in_entry;
PP_ReleaseSpaceCellNode(n);
n = next;
}
else
{
n = n->next_in_entry;
}
}
/* Insert new nodes */
for (i32 y = new_cell_p0.y; y <= new_cell_p1.y; ++y)
{
for (i32 x = new_cell_p0.x; x <= new_cell_p1.x; ++x)
{
if (x != 0 && y != 0 && (x < old_cell_p0.x || x > old_cell_p1.x || y < old_cell_p0.y || y > old_cell_p1.y))
{
/* Cell is outside of old AABB */
PP_AcquireSpaceCellNode(VEC2I32(x, y), entry);
}
}
}
entry->aabb = new_aabb;
}
////////////////////////////////////////////////////////////
//~ Iter
PP_SpaceIter PP_BeginSpaceIterAabb(PP_Space *space, Aabb aabb)
{
PP_SpaceIter iter = ZI;
f32 cell_size = space->cell_size;
iter.space = space;
iter.cell_start = PP_SpaceCellCoordsFromWorldCoords(cell_size, aabb.p0);
iter.cell_end = PP_SpaceCellCoordsFromWorldCoords(cell_size, aabb.p1);
if (iter.cell_start.x > iter.cell_end.x || iter.cell_start.y > iter.cell_end.y)
{
/* Swap cell_start & cell_end */
Vec2I32 tmp = iter.cell_start;
iter.cell_start = iter.cell_end;
iter.cell_end = tmp;
}
iter.aabb = aabb;
iter.cell_cur = iter.cell_start;
iter.cell_cur.x -= 1;
iter.cell_cur.y -= 1;
return iter;
}
PP_SpaceEntry *PP_NextSpaceIterAabb(PP_SpaceIter *iter)
{
PP_Space *space = iter->space;
Aabb iter_aabb = iter->aabb;
Vec2I32 cell_start = iter->cell_start;
Vec2I32 cell_end = iter->cell_end;
Vec2I32 cell_cur = iter->cell_cur;
i32 span = cell_end.x - cell_start.x;
PP_SpaceCellNode *next_node = 0;
if (cell_cur.x >= cell_start.x && cell_cur.x <= cell_end.x && cell_cur.y >= cell_start.y && cell_cur.y <= cell_end.y)
{
/* Started */
Assert(iter->prev != 0);
next_node = iter->prev->next_in_cell;
}
else if (cell_cur.x > cell_end.x || cell_cur.y > cell_end.y)
{
/* Ended */
return 0;
}
for (;;)
{
if (next_node)
{
PP_SpaceEntry *entry = next_node->entry;
Aabb entry_aabb = entry->aabb;
if (CLD_TestAabb(entry_aabb, iter_aabb))
{
break;
}
else
{
next_node = next_node->next_in_cell;
}
}
else
{
/* Reached end of cell, find next cell */
b32 nextx = (cell_cur.x + 1) <= cell_end.x;
b32 nexty = (nextx == 0) && ((cell_cur.y + 1) <= cell_end.y);
if (nextx || nexty)
{
cell_cur.x += nextx - (span * nexty);
cell_cur.y += nexty;
cell_cur.x += (cell_cur.x == 0);
cell_cur.y += (cell_cur.y == 0);
PP_SpaceCell *cell = PP_SpaceCellFromCellPos(space, cell_cur);
next_node = cell->first_node;
}
else
{
/* Reached end of iter */
cell_cur.x += 1;
cell_cur.y += 1;
break;
}
}
}
iter->prev = next_node;
iter->cell_cur = cell_cur;
return next_node ? next_node->entry : 0;
}

View File

@ -1,159 +0,0 @@
////////////////////////////////////////////////////////////
//~ Space entry types
Struct(PP_SpaceEntryKey)
{
u64 idx;
u64 gen;
};
Struct(PP_SpaceEntry)
{
b32 valid;
PP_SpaceEntryKey key;
struct PP_SpaceCellNode *first_node;
struct PP_SpaceCellNode *last_node;
Aabb aabb;
PP_EntKey ent;
PP_SpaceEntry *next_free;
};
////////////////////////////////////////////////////////////
//~ Space cell types
// Links a cell to a entry.
// Acts as both a node in the list of entries contained by the cell, and a node in the list of cells containing the entry. */
Struct(PP_SpaceCellNode)
{
PP_SpaceEntry *entry;
struct PP_SpaceCell *cell;
/* For list of all entries contained by cell */
PP_SpaceCellNode *prev_in_cell;
PP_SpaceCellNode *next_in_cell;
/* For list of all cells containing entry */
PP_SpaceCellNode *prev_in_entry;
PP_SpaceCellNode *next_in_entry;
PP_SpaceCellNode *next_free;
};
Struct(PP_SpaceCell)
{
b32 valid;
Vec2I32 pos;
PP_SpaceCellNode *first_node;
PP_SpaceCellNode *last_node;
struct PP_SpaceCellBin *bin;
PP_SpaceCell *prev_in_bin;
PP_SpaceCell *next_in_bin;
PP_SpaceCell *next_free;
};
Struct(PP_SpaceCellBin)
{
PP_SpaceCell *first_cell;
PP_SpaceCell *last_cell;
};
////////////////////////////////////////////////////////////
//~ Space types
Struct(PP_Space)
{
b32 valid;
f32 cell_size;
Arena *cell_arena;
PP_SpaceCellBin *bins;
i32 num_bins;
i32 num_bins_sqrt;
PP_SpaceCell *first_free_cell;
PP_SpaceCellNode *first_free_cell_node;
Arena *entry_arena;
u64 num_entries_reserved;
PP_SpaceEntry *entries;
PP_SpaceEntry *first_free_entry;
};
Struct(PP_SpaceIter)
{
Aabb aabb;
PP_Space *space;
Vec2I32 cell_start;
Vec2I32 cell_end;
Vec2I32 cell_cur;
PP_SpaceCellNode *prev;
};
////////////////////////////////////////////////////////////
//~ Nil types
/* Offset in bytes from start of space struct to start of entry array (assume adjacently allocated) */
#define PP_SpaceEntriesOffset (sizeof(PP_Space) + (sizeof(PP_Space) % alignof(PP_SpaceEntry)))
/* Accessed via NilEnt() */
extern Readonly PP_SpaceEntry PP_nil_space_entry;
extern Readonly PP_SpaceCell PP_nil_space_cell;
extern Readonly PP_Space PP_nil_space;
////////////////////////////////////////////////////////////
//~ Nil helpers
Inline PP_SpaceEntry *PP_NilSpaceEntry(void)
{
extern Readonly PP_SpaceEntry PP_nil_space_entry;
return &PP_nil_space_entry;
}
Inline PP_SpaceCell *PP_NilSpaceCell(void)
{
extern Readonly PP_SpaceCell PP_nil_space_cell;
return &PP_nil_space_cell;
}
Inline PP_Space *PP_NilSpace(void)
{
extern Readonly PP_Space PP_nil_space;
return &PP_nil_space;
}
////////////////////////////////////////////////////////////
//~ Space
PP_Space *PP_AcquireSpace(f32 cell_size, u32 num_bins_sqrt);
void PP_ReleaseSpace(PP_Space *space);
void PP_ResetSpace(PP_Space *space);
PP_Space *PP_SpaceFromEntry(PP_SpaceEntry *entry);
////////////////////////////////////////////////////////////
//~ Cell
Vec2I32 PP_SpaceCellCoordsFromWorldCoords(f32 cell_size, Vec2 world_pos);
i32 PP_SpaceBinIndexFromCellCoords(PP_Space *space, Vec2I32 cell_pos);
PP_SpaceCell *PP_SpaceCellFromCellPos(PP_Space *space, Vec2I32 cell_pos);
void PP_AcquireSpaceCellNode(Vec2I32 cell_pos, PP_SpaceEntry *entry);
void PP_ReleaseSpaceCellNode(PP_SpaceCellNode *n);
////////////////////////////////////////////////////////////
//~ Entry
PP_SpaceEntry *PP_SpaceEntryFromKey(PP_Space *space, PP_SpaceEntryKey key);
PP_SpaceEntry *PP_AcquireSpaceEntry(PP_Space *space, PP_EntKey ent);
void PP_ReleaseSpaceEntry(PP_SpaceEntry *entry);
void PP_UpdateSpaceEntryAabb(PP_SpaceEntry *entry, Aabb new_aabb);
////////////////////////////////////////////////////////////
//~ Iter
PP_SpaceIter PP_BeginSpaceIterAabb(PP_Space *space, Aabb aabb);
PP_SpaceEntry *PP_NextSpaceIterAabb(PP_SpaceIter *iter);
#define PP_EndSpaceIter(i)

File diff suppressed because it is too large Load Diff

View File

@ -1,68 +0,0 @@
////////////////////////////////////////////////////////////
//~ Acceleration structure types
/* Structure used to accelerate up entity lookup (rebuilt every step) */
/* TODO: Remove this and do something better. Just a hack to de-couple old sim ctx from step. */
Struct(PP_Accel)
{
PP_Space *space;
};
////////////////////////////////////////////////////////////
//~ Step ctx
Struct(PP_SimStepCtx)
{
b32 is_master;
PP_Accel *accel;
RandState rand; /* TODO: Replace with per-sim rand for deterministic rng */
PP_Snapshot *world; /* The world to simulate */
i64 sim_dt_ns; /* How much sim time should progress */
PP_Client *user_input_client; /* The client that contains input from the user thread */
PP_Client *master_client; /* The master client to read snapshots from (nil if world is master) */
PP_Client *publish_client; /* The publish client to write syncable state to (nil if skipping publish) */
};
////////////////////////////////////////////////////////////
//~ Accel operations
PP_Accel PP_AcquireAccel(void);
void PP_ReleaseAccel(PP_Accel *accel);
void PP_ResetAccel(PP_Snapshot *ss, PP_Accel *accel);
////////////////////////////////////////////////////////////
//~ Spawn test operations
PP_Ent *PP_SpawnTestSmg(PP_Ent *parent);
PP_Ent *PP_SpawnTestLauncher(PP_Ent *parent);
PP_Ent *PP_SpawnTestChucker(PP_Ent *parent);
PP_Ent *PP_SpawnTestEmployee(PP_Ent *parent);
PP_Ent *PP_SpawnTestCamera(PP_Ent *parent, PP_Ent *follow);
PP_Ent *PP_SpawnTestExplosion(PP_Ent *parent, Vec2 pos, f32 strength, f32 radius);
void PP_TeleportTest(PP_Ent *ent, Vec2 pos);
void PP_SpawnTestEnts1(PP_Ent *parent, Vec2 pos);
void PP_SpawnTestEnts2(PP_Ent *parent, Vec2 pos);
void PP_SpawnTestEnts3(PP_Ent *parent, Vec2 pos);
void PP_SpawnTestEnts4(PP_Ent *parent, Vec2 pos);
void PP_SpawnTestTile(PP_Snapshot *world, Vec2 world_pos);
void PP_ClearLevelTest(PP_SimStepCtx *ctx);
////////////////////////////////////////////////////////////
//~ Tile test operations
MergesortCompareFuncDef(PP_SortTileXCmp, arg_a, arg_b, _);
MergesortCompareFuncDef(PP_SortTileYCmp, arg_a, arg_b, _);
void PP_GenerateTestWalls(PP_Snapshot *world);
////////////////////////////////////////////////////////////
//~ Collision response
PP_CollisionCallbackFuncDef(PP_OnEntCollision, data, step_ctx);
////////////////////////////////////////////////////////////
//~ Step
void PP_StepSim(PP_SimStepCtx *ctx);

View File

@ -1,144 +0,0 @@
////////////////////////////////////////////////////////////
//~ Console
UI_Box *PP_BuildDebugConsole(b32 minimized)
{
/* TODO: Remove this whole thing */
__prof;
PP_SharedUserState *g = &PP_shared_user_state;
TempArena scratch = BeginScratchNoConflict();
// i32 console_level = minimized ? LogLevel_Success : LogLevel_Debug;
i32 console_level = LogLevel_Debug;
u32 colors[LogLevel_COUNT][2] = ZI;
SetBytes(colors, 0xFF, sizeof(colors));
/* Debug colors */
colors[LogLevel_Debug][0] = Rgb32F(0.4, 0.1, 0.4);
colors[LogLevel_Debug][1] = Rgb32F(0.5, 0.2, 0.5);
/* Info colors */
colors[LogLevel_Info][0] = Rgb32F(0.4, 0.4, 0.4);
colors[LogLevel_Info][1] = Rgb32F(0.5, 0.5, 0.5);
/* Success colors */
colors[LogLevel_Success][0] = Rgb32F(0.1, 0.3, 0.1);
colors[LogLevel_Success][1] = Rgb32F(0.2, 0.4, 0.2);
/* Warning colors */
colors[LogLevel_Warning][0] = Rgb32F(0.4, 0.4, 0.1);
colors[LogLevel_Warning][1] = Rgb32F(0.5, 0.5, 0.2);
/* Error colors */
colors[LogLevel_Error][0] = Rgb32F(0.4, 0.1, 0.1);
colors[LogLevel_Error][1] = Rgb32F(0.5, 0.2, 0.2);
i64 max_time_ns = I64Max;
i64 fade_time_ns = max_time_ns;
if (minimized)
{
max_time_ns = NsFromSeconds(10);
fade_time_ns = max_time_ns;
}
f32 fade_curve = 0.5;
i64 now_ns = TimeNs();
UI_Box *console_box = 0;
{
UI_SetNext(Border, 0);
if (minimized)
{
UI_SetNext(BackgroundColor, 0);
UI_SetNext(Width, UI_PIX(500, 0));
UI_SetNext(Height, UI_FIT(1));
}
else
{
UI_SetNext(BackgroundColor, Rgba32F(1, 1, 1, 0.02));
UI_SetNext(Width, UI_FILL(1, 0));
UI_SetNext(Height, UI_FIT(1));
}
console_box = UI_BuildColumn(UI_KeyF("Console box"));
UI_PushCP(console_box);
{
/* Gather display logs */
u64 max = 20;
u64 display_count = 0;
LogEvent *display_logs = PushStructs(scratch.arena, LogEvent, max);
{
b32 done = 0;
if (minimized)
{
max = 5;
}
LogEventsArray logs = GetLogEvents();
for (u64 i = logs.count; i-- > 0 && display_count < max && !done;)
{
LogEvent ev = logs.logs[i];
if (ev.time_ns > (now_ns - max_time_ns))
{
if (ev.level <= console_level)
{
display_logs[display_count] = ev;
++display_count;
}
}
else
{
done = 1;
}
}
}
/* Display logs in reverse */
for (u64 i = display_count; i-- > 0;)
{
LogEvent log = display_logs[i];
f32 opacity = 0.75;
f32 lin = 1.0 - ClampF64((f64)(now_ns - log.time_ns) / (f64)fade_time_ns, 0, 1);
opacity *= PowF32(lin, fade_curve);
String text = log.msg;
if (!minimized)
{
DateTime datetime = log.datetime;
text = StringF(
scratch.arena,
"[%F:%F:%F.%F] %F",
FmtUintZ(datetime.hour, 2),
FmtUintZ(datetime.minute, 2),
FmtUintZ(datetime.second, 2),
FmtUintZ(datetime.milliseconds, 3),
FmtString(text)
);
}
UI_PushCP(0);
{
UI_Push(Tint, Alpha32F(0xFFFFFFFF, opacity));
{
u32 color = colors[log.level][log.level_id % 2];
UI_Push(BackgroundColor, color);
UI_Push(Width, UI_FILL(1, 0));
UI_Push(Height, UI_FNT(1.5, 1));
UI_Push(BorderColor, Rgba32F(0.25, 0.25, 0.25, 1));
UI_Push(Rounding, UI_RPIX(0));
UI_Push(Border, 1);
UI_Push(ChildAlignment, UI_Alignment_Left);
UI_PushCP(UI_BuildRow(UI_NilKey));
{
// UI_SetNext(Height, UI_PIX(100, 0));
UI_BuildSpacer(UI_PIX(10, 0));
UI_Push(BackgroundColor, 0);
UI_Push(Border, 0);
UI_Push(Text, text);
UI_Push(Width, UI_FILL(1, 0));
UI_Push(Height, UI_FIT(1));
UI_Push(Flags, UI_BoxFlag_DrawText);
UI_BuildBox(UI_NilKey);
}
UI_PopCP();
}
}
UI_PopCP();
}
}
UI_PopCP();
}
EndScratch(scratch);
return console_box;
}

View File

@ -1,4 +0,0 @@
////////////////////////////////////////////////////////////
//~ Console
UI_Box *PP_BuildDebugConsole(b32 minimized);

View File

@ -1,11 +1,11 @@
////////////////////////////////////////////////////////////
//~ Serialize
String SETTINGS_StringFromWindowSettings(Arena *arena, const P_WindowSettings *settings)
String SETTINGS_StringFromWindowSettings(Arena *arena, const PLT_WindowSettings *settings)
{
String minimized = settings->flags & P_WindowSettingsFlag_Minimized ? Lit("true") : Lit("false");
String maximized = settings->flags & P_WindowSettingsFlag_Maximized ? Lit("true") : Lit("false");
String fullscreen = settings->flags & P_WindowSettingsFlag_Fullscreen ? Lit("true") : Lit("false");
String minimized = settings->flags & PLT_WindowSettingsFlag_Minimized ? Lit("true") : Lit("false");
String maximized = settings->flags & PLT_WindowSettingsFlag_Maximized ? Lit("true") : Lit("false");
String fullscreen = settings->flags & PLT_WindowSettingsFlag_Fullscreen ? Lit("true") : Lit("false");
i32 x = settings->floating_x;
i32 y = settings->floating_y;
i32 width = settings->floating_width;
@ -43,14 +43,14 @@ String SETTINGS_StringFromWindowSettings(Arena *arena, const P_WindowSettings *s
////////////////////////////////////////////////////////////
//~ Deserialize
P_WindowSettings *SETTINGS_WindowSettingsFromString(Arena *arena, String src, String *error_out)
PLT_WindowSettings *SETTINGS_WindowSettingsFromString(Arena *arena, String src, String *error_out)
{
TempArena scratch = BeginScratch(arena);
String error = ZI;
JSON_Error json_error = ZI;
P_WindowSettings *settings = PushStruct(arena, P_WindowSettings);
PLT_WindowSettings *settings = PushStruct(arena, PLT_WindowSettings);
JSON_Result parse_result = JSON_BlobFromString(scratch.arena, src);
if (parse_result.errors.count > 0)
@ -92,7 +92,7 @@ P_WindowSettings *SETTINGS_WindowSettingsFromString(Arena *arena, String src, St
}
if (child->value.boolean)
{
settings->flags |= P_WindowSettingsFlag_Maximized;
settings->flags |= PLT_WindowSettingsFlag_Maximized;
}
found_maximized = 1;
}
@ -105,7 +105,7 @@ P_WindowSettings *SETTINGS_WindowSettingsFromString(Arena *arena, String src, St
}
if (child->value.boolean)
{
settings->flags |= P_WindowSettingsFlag_Fullscreen;
settings->flags |= PLT_WindowSettingsFlag_Fullscreen;
}
found_fullscreen = 1;
}

View File

@ -1,2 +1,2 @@
String SETTINGS_StringFromWindowSettings(Arena *arena, const P_WindowSettings *settings);
P_WindowSettings *SETTINGS_WindowSettingsFromString(Arena *arena, String src, String *error_out);
String SETTINGS_StringFromWindowSettings(Arena *arena, const PLT_WindowSettings *settings);
PLT_WindowSettings *SETTINGS_WindowSettingsFromString(Arena *arena, String src, String *error_out);