net_win32 layer

This commit is contained in:
jacob 2026-01-15 13:22:24 -06:00
parent b73daaf1f1
commit fe5aca14d2
24 changed files with 1299 additions and 775 deletions

View File

@ -25,7 +25,10 @@ if "%PROCESSOR_ARCHITECTURE%" equ "AMD64" (
) )
where /Q cl.exe || ( where /Q cl.exe || (
echo ************************************************************************** echo **************************************************************************
echo WARNING: cl.exe not found, attempting to locate Visual Studio installation echo cl.exe not found in the current environment, attempting to locate Visual
echo Studio installation. Run this script from a devenv-equipped environment
echo to remove this notice and speed up build time.
echo.
set __VSCMD_ARG_NO_LOGO=1 set __VSCMD_ARG_NO_LOGO=1
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
@ -37,6 +40,7 @@ where /Q cl.exe || (
call "!VS!\Common7\Tools\VsDevCmd.bat" -arch=%HOST_ARCH% -host_arch=%HOST_ARCH% -startdir=none -no_logo || exit /b 1 call "!VS!\Common7\Tools\VsDevCmd.bat" -arch=%HOST_ARCH% -host_arch=%HOST_ARCH% -startdir=none -no_logo || exit /b 1
echo Visual studio development environment activated echo Visual studio development environment activated
echo ************************************************************************** echo **************************************************************************
echo.
) )
::- Meta build ::- Meta build

View File

@ -439,15 +439,6 @@
#define IsIndexable(a) (sizeof(a[0]) != 0) #define IsIndexable(a) (sizeof(a[0]) != 0)
#define IsFixedArray(a) (IsIndexable(a) && (((void *)&a) == ((void *)a))) #define IsFixedArray(a) (IsIndexable(a) && (((void *)&a) == ((void *)a)))
//- offsetof
#if IsCompilerMsvc
#ifdef _CRT_USE_BUILTIN_OFFSETOF
#define offsetof(type, field) __builtin_offsetof(type, field)
#else
#define offsetof(type, field) ((u64)&(((type *)0)->field))
#endif
#endif
//- struct region //- struct region
#define BeginFieldRegion(name) i8 __begfieldreg__##name #define BeginFieldRegion(name) i8 __begfieldreg__##name
#define EndFieldRegion(name) i8 __endfieldreg__##name #define EndFieldRegion(name) i8 __endfieldreg__##name

View File

@ -6,25 +6,25 @@
#define COBJMACROS #define COBJMACROS
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define UNICODE #define UNICODE
#pragma warning(push, 0) // #pragma warning(push, 0)
#include <Windows.h> #include <Windows.h>
#include <combaseapi.h> #include <combaseapi.h>
#include <dcommon.h> #include <dcommon.h>
#include <initguid.h> #include <initguid.h>
#include <unknwn.h> #include <unknwn.h>
#include <objbase.h> #include <objbase.h>
#include <uuids.h> #include <uuids.h>
#include <Knownfolders.h> #include <Knownfolders.h>
#include <WinSock2.h> #include <WinSock2.h>
#include <TlHelp32.h> #include <TlHelp32.h>
#include <WS2tcpip.h> #include <WS2tcpip.h>
#include <windowsx.h> #include <windowsx.h>
#include <ShlObj_core.h> #include <ShlObj_core.h>
#include <fileapi.h> #include <fileapi.h>
#include <dwmapi.h> #include <dwmapi.h>
#include <avrt.h> #include <avrt.h>
#include <shellapi.h> #include <shellapi.h>
#pragma warning(pop) // #pragma warning(pop)
#ifndef BCRYPT_RNG_ALG_HANDLE #ifndef BCRYPT_RNG_ALG_HANDLE
#define BCRYPT_RNG_ALG_HANDLE ((void *)0x00000081) #define BCRYPT_RNG_ALG_HANDLE ((void *)0x00000081)

View File

@ -249,9 +249,9 @@ Enum(G_Layout)
{ {
G_Layout_NoChange, G_Layout_NoChange,
// Allows a resource to be used on any queue with any access type, as long // "Simultaneous" allows a resource to be used on any queue with any access
// as there is only one writer at a time, and the writer is not writing to // type, as long as there is only one writer at a time, and the writer is not
// any texels currently being read. // writing to any texels currently being read.
// Resources cannot transition to/from this layout. They must be created // Resources cannot transition to/from this layout. They must be created
// with it and are locked to it. // with it and are locked to it.
G_Layout_Simultaneous, // D3D12_BARRIER_LAYOUT_COMMON + D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS G_Layout_Simultaneous, // D3D12_BARRIER_LAYOUT_COMMON + D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS
@ -626,77 +626,77 @@ void *G_HostPointerFromResource(G_ResourceHandle resource);
u32 G_PushRef(G_ArenaHandle arena, G_ResourceHandle resource, G_RefDesc desc); u32 G_PushRef(G_ArenaHandle arena, G_ResourceHandle resource, G_RefDesc desc);
#define G_PushStructuredBufferRef(arena, resource, type, ...)(G_StructuredBufferRef) { \ #define G_PushStructuredBufferRef(arena, resource, type, ...) (G_StructuredBufferRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_StructuredBuffer, .element_size = sizeof(type), __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_StructuredBuffer, .element_size = sizeof(type), __VA_ARGS__ } \
) \ ) \
} }
#define G_PushRWStructuredBufferRef(arena, resource, type, ...)(G_RWStructuredBufferRef) { \ #define G_PushRWStructuredBufferRef(arena, resource, type, ...) (G_RWStructuredBufferRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_RWStructuredBuffer, .element_size = sizeof(type), __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_RWStructuredBuffer, .element_size = sizeof(type), __VA_ARGS__ } \
) \ ) \
} }
#define G_PushByteAddressBufferRef(arena, resource, ...)(G_ByteAddressBufferRef) { \ #define G_PushByteAddressBufferRef(arena, resource, ...) (G_ByteAddressBufferRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_ByteAddressBuffer, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_ByteAddressBuffer, __VA_ARGS__ } \
) \ ) \
} }
#define G_PushRWByteAddressBufferRef(arena, resource, ...)(G_RWByteAddressBufferRef) { \ #define G_PushRWByteAddressBufferRef(arena, resource, ...) (G_RWByteAddressBufferRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_RWByteAddressBuffer, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_RWByteAddressBuffer, __VA_ARGS__ } \
) \ ) \
} }
#define G_PushTexture1DRef(arena, resource, ...)(G_Texture1DRef) { \ #define G_PushTexture1DRef(arena, resource, ...) (G_Texture1DRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_Texture1D, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_Texture1D, __VA_ARGS__ } \
) \ ) \
} }
#define G_PushRWTexture1DRef(arena, resource, ...)(G_RWTexture1DRef) { \ #define G_PushRWTexture1DRef(arena, resource, ...) (G_RWTexture1DRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_RWTexture1D, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_RWTexture1D, __VA_ARGS__ } \
) \ ) \
} }
#define G_PushTexture2DRef(arena, resource, ...)(G_Texture2DRef) { \ #define G_PushTexture2DRef(arena, resource, ...) (G_Texture2DRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_Texture2D, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_Texture2D, __VA_ARGS__ } \
) \ ) \
} }
#define G_PushRWTexture2DRef(arena, resource, ...)(G_RWTexture2DRef) { \ #define G_PushRWTexture2DRef(arena, resource, ...) (G_RWTexture2DRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_RWTexture2D, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_RWTexture2D, __VA_ARGS__ } \
) \ ) \
} }
#define G_PushTexture3DRef(arena, resource, ...)(G_Texture3DRef) { \ #define G_PushTexture3DRef(arena, resource, ...) (G_Texture3DRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_Texture3D, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_Texture3D, __VA_ARGS__ } \
) \ ) \
} }
#define G_PushRWTexture3DRef(arena, resource, ...)(G_RWTexture3DRef) { \ #define G_PushRWTexture3DRef(arena, resource, ...) (G_RWTexture3DRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_RWTexture3D, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_RWTexture3D, __VA_ARGS__ } \
) \ ) \
} }
#define G_PushSamplerStateRef(arena, resource, ...)(G_SamplerStateRef) { \ #define G_PushSamplerStateRef(arena, resource, ...) (G_SamplerStateRef) { \
.v = G_PushRef( \ .v = G_PushRef( \
(arena), (resource), \ (arena), (resource), \
(G_RefDesc) { .kind = G_RefKind_SamplerState, __VA_ARGS__ } \ (G_RefDesc) { .kind = G_RefKind_SamplerState, __VA_ARGS__ } \
@ -711,7 +711,7 @@ u32 G_PushRef(G_ArenaHandle arena, G_ResourceHandle resource, G_RefDesc desc);
G_CommandListHandle G_PrepareCommandList(G_QueueKind queue); G_CommandListHandle G_PrepareCommandList(G_QueueKind queue);
i64 G_CommitCommandList(G_CommandListHandle cl); i64 G_CommitCommandList(G_CommandListHandle cl);
//- Cpu -> Gpu copy //- Cpu -> Gpu staged copy
void G_CopyCpuToBuffer(G_CommandListHandle cl, G_ResourceHandle dst, u64 dst_offset, void *src, RngU64 src_copy_range); void G_CopyCpuToBuffer(G_CommandListHandle cl, G_ResourceHandle dst, u64 dst_offset, void *src, RngU64 src_copy_range);
void G_CopyCpuToTexture(G_CommandListHandle cl, G_ResourceHandle dst, Vec3I32 dst_offset, void *src, Vec3I32 src_dims, Rng3I32 src_copy_range); void G_CopyCpuToTexture(G_CommandListHandle cl, G_ResourceHandle dst, Vec3I32 dst_offset, void *src, Vec3I32 src_dims, Rng3I32 src_copy_range);
@ -731,7 +731,7 @@ void G_SetConstant_(G_CommandListHandle cl, i32 slot, void *src_32bit, u32 size)
name##__shaderconstanttype __src; \ name##__shaderconstanttype __src; \
__src.v = value; \ __src.v = value; \
G_SetConstant_((cl), (name), &__src, sizeof(__src)); \ G_SetConstant_((cl), (name), &__src, sizeof(__src)); \
} while (0) } while (0)
//- Memory sync //- Memory sync

View File

@ -2429,7 +2429,7 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle)
return completion_target; return completion_target;
} }
//- Cpu -> Gpu copy //- Cpu -> Gpu staged copy
void G_CopyCpuToBuffer(G_CommandListHandle cl_handle, G_ResourceHandle dst_handle, u64 dst_offset, void *src, RngU64 src_copy_range) void G_CopyCpuToBuffer(G_CommandListHandle cl_handle, G_ResourceHandle dst_handle, u64 dst_offset, void *src, RngU64 src_copy_range)
{ {
@ -2763,7 +2763,6 @@ G_QueueCompletions G_CompletionTargetsFromQueues(G_QueueMask queue_mask)
void G_SyncEx(G_QueueBarrierDesc desc) void G_SyncEx(G_QueueBarrierDesc desc)
{ {
u64 fences_count = 0; u64 fences_count = 0;
ID3D12Fence *fences[G_QueueKind_COUNT] = Zi; ID3D12Fence *fences[G_QueueKind_COUNT] = Zi;
i64 fence_targets[G_QueueKind_COUNT] = Zi; i64 fence_targets[G_QueueKind_COUNT] = Zi;

View File

@ -35,8 +35,8 @@ Struct(G_SamplerStateRef) { u32 v; };
// //
// D3D12 exposes 64 root constants and Vulkan exposes 32 push constants. // D3D12 exposes 64 root constants and Vulkan exposes 32 push constants.
// Supposedly amd hardware will start spilling constants once there // Supposedly AMD hardware will start spilling constants once there
// are more than 12: https://gpuopen.com/learn/rdna-performance-guide/ // are more than 12 - https://gpuopen.com/learn/rdna-performance-guide/
// //
#define G_NumGeneralPurposeConstants (8) // Constants available for any usage #define G_NumGeneralPurposeConstants (8) // Constants available for any usage
#define G_NumReservedConstants (4) // Constants reserved for internal usage by the GPU layer #define G_NumReservedConstants (4) // Constants reserved for internal usage by the GPU layer
@ -77,7 +77,7 @@ G_ForceDeclConstant(f32, G_ShaderConst_TweakF32, 10
#if IsLanguageG #if IsLanguageG
// TODO: Non-uniform resource access currently is assumed as the default // TODO: Non-uniform resource access currently is assumed as the default
// behavior. We may want to add explicit "uniform" variants for // behavior. We may want to add explicit "uniform" variants for
// optimization on amd in the future. // optimization on AMD in the future.
template<typename T> StructuredBuffer<T> G_Dereference(G_StructuredBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } template<typename T> StructuredBuffer<T> G_Dereference(G_StructuredBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; }
template<typename T> RWStructuredBuffer<T> G_Dereference(G_RWStructuredBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } template<typename T> RWStructuredBuffer<T> G_Dereference(G_RWStructuredBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; }

View File

@ -326,7 +326,7 @@ void BuildEntryPoint(WaveLaneCtx *lane)
layer_name = Lit("pp"); layer_name = Lit("pp");
if (lane->idx == 0) if (lane->idx == 0)
{ {
EchoLine(Lit("No layer supplied, assuming \"pp\" build")); EchoLine(Lit("No layer specified, assuming \"pp\" build"));
} }
} }
cmdline.leaf_layer_name = layer_name; cmdline.leaf_layer_name = layer_name;
@ -440,6 +440,8 @@ void BuildEntryPoint(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Phase 1/3: Prep //- Phase 1/3: Prep
// Build phases:
//
// Phase 1/3: Prep (narrow) // Phase 1/3: Prep (narrow)
// - Parse layers // - Parse layers
// - Generate final C file // - Generate final C file

View File

@ -1,318 +1,52 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Channel ID //~ Opaque types
Struct(N_ChannelId) Struct(NET_PipeHandle) { u64 v; };
{
u32 gen; #define NET_IsPipeNil(h) ((h).v == 0)
u32 idx;
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Host command types //~ Key types
Enum(N_CmdKind) Struct(NET_Key)
{ {
N_CmdKind_None, u64 v;
N_CmdKind_TryConnect,
N_CmdKind_ConnectSuccess,
N_CmdKind_Disconnect,
N_CmdKind_Heartbeat,
N_CmdKind_Write
}; };
Enum(N_WriteFlag) #define NET_NilKey ((NET_Key) { 0 })
{ #define NET_IsKeyNil(k) ((k).v == 0)
N_WriteFlag_None = 0,
N_WriteFlag_Reliable = (1 << 0)
};
Struct(N_Cmd)
{
N_CmdKind kind;
N_ChannelId channel_id;
u16 heartbeat_id;
u16 heartbeat_ack_id;
b32 write_reliable;
String write_msg;
N_Cmd *next;
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Event types //~ Message types
Enum(N_EventKind) Struct(NET_Msg)
{ {
N_EventKind_None, NET_Msg *next;
N_EventKind_ChannelOpened, NET_Msg *prev;
N_EventKind_ChannelClosed,
N_EventKind_Msg
};
Struct(N_Event) NET_Key src;
{
N_EventKind kind;
N_ChannelId channel_id;
String msg;
N_Event *next;
};
Struct(N_EventList)
{
N_Event *first;
N_Event *last;
};
Struct(N_ChannelLookupBin)
{
struct N_Channel *first;
struct N_Channel *last;
};
////////////////////////////////////////////////////////////
//~ Packet types
#define N_PacketMagic 0xd9e3b8b6
#define N_MaxPacketChunkLen 1024
#define N_MaxPacketLen 1280 // Give enough space for msg chunk + header
Enum(N_PacketKind)
{
N_PacketKind_None,
N_PacketKind_TryConnect,
N_PacketKind_ConnectSuccess,
N_PacketKind_Disconnect,
N_PacketKind_Heartbeat,
N_PacketKind_MsgChunk
};
Enum(N_PacketFlag)
{
N_PacketFlag_None = 0,
N_PacketFlag_Reliable = (1 << 0)
};
Struct(N_SndPacket)
{
N_SndPacket *next;
u64 seq;
u64 data_len;
u8 data[N_MaxPacketLen];
};
Struct(N_RcvPacket)
{
PLT_Sock *sock;
PLT_Address address;
String data; String data;
N_RcvPacket *next;
}; };
Struct(N_RcvBuffer) Struct(NET_MsgList)
{ {
Arena *arena; i64 count;
N_RcvPacket *first_packet; NET_Msg *first;
N_RcvPacket *last_packet; NET_Msg *last;
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Channel types //~ @hookdecl Bootstrap
Struct(N_Channel) void NET_Bootstrap(void);
{
N_ChannelId id;
b32 valid;
b32 connected;
struct N_Host *host;
N_Channel *next_free;
PLT_Address address;
u64 address_hash;
N_Channel *next_address_hash;
N_Channel *prev_address_hash;
// NOTE: Packets are allocated in host's `arena`
N_SndPacket *first_reliable_packet;
N_SndPacket *last_reliable_packet;
N_SndPacket *first_unreliable_packet;
N_SndPacket *last_unreliable_packet;
u64 num_reliable_packets;
u64 num_unreliable_packets;
// NOTE: Msg assemblers are allocated in host's `arena`
struct N_MsgAssembler *least_recent_msg_assembler;
struct N_MsgAssembler *most_recent_msg_assembler;
u16 last_heartbeat_received_id;
u16 last_heartbeat_acked_id;
i64 last_heartbeat_acked_ns;
i64 last_heartbeat_rtt_ns;
u64 last_sent_msg_id;
u64 their_acked_seq;
u64 our_acked_seq;
u64 last_sent_seq;
i64 last_packet_received_ns;
};
Struct(N_ChannelNode)
{
N_Channel *channel;
N_ChannelNode *next;
};
Struct(N_ChannelList)
{
N_ChannelNode *first;
N_ChannelNode *last;
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Message asssembler types //~ @hookdecl Net ops
Struct(N_MsgAssembler) NET_PipeHandle NET_AcquirePipe(void);
{
N_Channel *channel;
b32 is_reliable;
// Free list void NET_Bind(NET_PipeHandle pipe, u64 port);
N_MsgAssembler *next_free; u64 NET_BoundPortFromPipe(NET_PipeHandle pipe_handle);
// Bucket list void NET_Push(NET_PipeHandle pipe, NET_Key dst, String data, b32 unreliable);
N_MsgAssembler *next_hash; NET_MsgList NET_Pop(Arena *arena, NET_PipeHandle pipe);
N_MsgAssembler *prev_hash;
// Channel list
N_MsgAssembler *less_recent;
N_MsgAssembler *more_recent;
u64 msg_id;
u64 hash;
u64 last_chunk_len;
u64 num_chunks_total;
u64 num_chunks_received;
i64 touched_ns;
BuddyBlock *buddy_block;
u8 *chunk_bitmap;
u8 *chunk_data;
};
Struct(N_MsgAssemblerLookupBin)
{
N_MsgAssembler *first;
N_MsgAssembler *last;
};
////////////////////////////////////////////////////////////
//~ Host types
#define N_NumChannelLookupBins 512
#define N_NumMsgAssemblerLookupBins 16384
Struct(N_Host)
{
Arena *arena;
PLT_Sock *sock;
BuddyCtx *buddy; // For storing msg assembler data
Arena *cmd_arena;
N_Cmd *first_cmd;
N_Cmd *last_cmd;
N_Cmd *first_free_cmd;
Arena *channel_arena;
N_Channel *channels;
N_Channel *first_free_channel;
u64 num_channels_reserved;
N_SndPacket *first_free_packet; // Acquired in `arena`
N_MsgAssembler *first_free_msg_assembler; // Acquired in `arena`
N_ChannelLookupBin *channel_lookup_bins; // Acquired in `arena`
u64 num_channel_lookup_bins;
N_MsgAssemblerLookupBin *msg_assembler_lookup_bins; // Acquired in `arena`
u64 num_msg_assembler_lookup_bins;
// Double buffer for incoming data
Mutex rcv_buffer_write_mutex;
N_RcvBuffer *rcv_buffer_read;
N_RcvBuffer *rcv_buffer_write;
u64 bytes_received;
u64 bytes_sent;
};
////////////////////////////////////////////////////////////
//~ Nil constants
Readonly Global N_Channel N_nil_channel = { .valid = 0 };
////////////////////////////////////////////////////////////
//~ Host initialization
N_Host *N_AcquireHost(u16 listen_port);
void N_ReleaseHost(N_Host *host);
////////////////////////////////////////////////////////////
//~ Channel
#define N_NilChannelId (N_ChannelId) { .gen = 0, .idx = 0 }
#define N_AllChannelsId (N_ChannelId) { .gen = U32Max, .idx = U32Max }
#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(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, PLT_Address address);
void N_ReleaseChannel(N_Channel *channel);
////////////////////////////////////////////////////////////
//~ Message assembler
u64 N_HashFromMsg(N_ChannelId channel_id, u64 msg_id);
N_MsgAssembler *N_MsgAssemblerFromMsg(N_Host *host, N_ChannelId channel_id, u64 msg_id);
N_MsgAssembler *N_AcquireMsgAssembler(N_Channel *channel, u64 msg_id, u64 chunk_count, u64 now_ns, b32 is_reliable);
void N_ReleaseMessageAssembler(N_MsgAssembler *ma);
void N_TouchMessageAssembler(N_MsgAssembler *ma, i64 now_ns);
b32 N_IsChunkFilled(N_MsgAssembler *ma, u64 chunk_id);
void N_MarkChunkReceived(N_MsgAssembler *ma, u64 chunk_id);
////////////////////////////////////////////////////////////
//~ Packet
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, 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);
////////////////////////////////////////////////////////////
//~ Channel info
i64 N_GetChannelLastRttNs(N_Host *host, N_ChannelId channel_id);
////////////////////////////////////////////////////////////
//~ Update
N_Event *N_PushEvent(Arena *arena, N_EventList *list);
N_EventList N_BeginUpdate(Arena *arena, N_Host *host);
void N_EndUpdate(N_Host *host);

View File

@ -1,16 +1,13 @@
@Layer net @Layer net
//////////////////////////////
//- Dependencies
@Dep platform
////////////////////////////// //////////////////////////////
//- Api //- Api
@IncludeC net.h @IncludeC net.h
@Bootstrap NET_Bootstrap
////////////////////////////// //////////////////////////////
//- Impl //- Impl
@IncludeC net.c @DefaultDownstream Win32 net_win32

View File

@ -0,0 +1,244 @@
NET_W32_Ctx NET_W32 = Zi;
////////////////////////////////////////////////////////////
//~ @hookimpl Bootstrap
void NET_Bootstrap(void)
{
Arena *perm = PermArena();
WSADATA wsa = Zi;
i32 err = WSAStartup(MAKEWORD(2,2), &wsa);
if (WSAStartup(MAKEWORD(2,2), &wsa) != 0)
{
Panic(StringF(perm, "Failed to initialize WinSock (error code - %F)", FmtSint(err)));
}
DispatchWave(Lit("Net"), 1, NET_W32_TickForever, 0);
}
////////////////////////////////////////////////////////////
//~ Helpers
NET_W32_Pipe *NET_W32_PipeFromHandle(NET_PipeHandle pipe_handle)
{
return (NET_W32_Pipe *)pipe_handle.v;
}
////////////////////////////////////////////////////////////
//~ @hookimpl Net ops
NET_PipeHandle NET_AcquirePipe(void)
{
Arena *perm = PermArena();
NET_W32_Pipe *pipe = PushStruct(perm, NET_W32_Pipe);
return (NET_PipeHandle) { .v = (u64) pipe };
}
void NET_Bind(NET_PipeHandle pipe_handle, u64 port)
{
// TODO: Mabye remove binding from the net interface entirely?
// Messages can just be popped from specific ports (with port 0
// always returning no messages). Ephemeral ports can be "fetched",
// which under the hood binds them. A caching/timeout mechanism then
// just closes sockets as needed.
TempArena scratch = BeginScratchNoConflict();
NET_W32_Pipe *pipe = NET_W32_PipeFromHandle(pipe_handle);
b32 is_ephemeral = port == 0;
// FIXME: Retry on timeout
if (!pipe->udp || (!is_ephemeral && pipe->bound_port != port))
{
b32 ok = 1;
String port_str = StringF(scratch.arena, "%F", FmtUint(port));
char *port_cstr = CstrFromString(scratch.arena, port_str);
if (pipe->udp)
{
closesocket(pipe->udp);
pipe->bound_port = 0;
pipe->udp = 0;
}
//- Init bind address
struct addrinfo hints = Zi;
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol= IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
struct addrinfo *ai = 0;
if (ok)
{
ok = getaddrinfo(0, port_cstr, &hints, &ai) == 0;
}
//- Create udp socket
SOCKET sock = 0;
if (ok)
{
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
ok = sock != INVALID_SOCKET;
}
//- Enable address reuse
if (ok)
{
b32 reuse = 1;
ok = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == 0;
}
//- Enable dual stack
if (ok)
{
DWORD v6_only = 0;
ok = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6_only, sizeof(v6_only)) == 0;
}
//- Bind
if (ok)
{
ok = bind(sock, ai->ai_addr, (i32)ai->ai_addrlen) == 0;
}
//- Enable non-blocking
if (ok)
{
u_long nonblocking = 1;
ok = ioctlsocket(sock, FIONBIO, &nonblocking) == 0;
}
//- Fetch bound port
u64 bound_port = 0;
{
struct sockaddr_storage ss = Zi;
if (ok)
{
i32 ss_sizeof = sizeof(ss);
ok = getsockname(sock, (struct sockaddr *)&ss, &ss_sizeof) != SOCKET_ERROR;
}
if (ok)
{
if (ss.ss_family == AF_INET)
{
struct sockaddr_in *a = (struct sockaddr_in *)&ss;
bound_port = ntohs(a->sin_port);
}
else if (ss.ss_family == AF_INET6)
{
struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)&ss;
bound_port = ntohs(a6->sin6_port);
}
else
{
ok = 0;
}
}
}
//- Finalize
if (ok)
{
pipe->bound_port = bound_port;
pipe->udp = sock;
}
else
{
if (sock != INVALID_SOCKET)
{
closesocket(sock);
}
}
if (ai)
{
freeaddrinfo(ai);
}
}
EndScratch(scratch);
}
u64 NET_BoundPortFromPipe(NET_PipeHandle pipe_handle)
{
// TODO: Instead maybe return "BindingStatus", which includes port + binding error/progress
NET_W32_Pipe *pipe = NET_W32_PipeFromHandle(pipe_handle);
return pipe->bound_port;
}
void NET_Push(NET_PipeHandle pipe_handle, NET_Key dst, String data, b32 unreliable)
{
NET_W32_Pipe *pipe = NET_W32_PipeFromHandle(pipe_handle);
if (!pipe->udp)
{
// Bind to ephemeral port if not bound
NET_Bind(pipe_handle, 0);
}
if (pipe->udp)
{
}
}
NET_MsgList NET_Pop(Arena *arena, NET_PipeHandle pipe_handle)
{
NET_MsgList result = Zi;
return result;
}
////////////////////////////////////////////////////////////
//~ Net worker
void NET_W32_TickForever(WaveLaneCtx *lane)
{
for (;;)
{
TempArena scratch = BeginScratchNoConflict();
// TODO: Block until send/recv/signal
//////////////////////////////
//- Pop pipes
NET_W32_Pipe *first_pipe = 0;
NET_W32_Pipe *last_pipe = 0;
//////////////////////////////
//- Pop cmds
// NET_W32_Cmd *first_cmd = 0;
// NET_W32_Cmd *last_cmd = 0;
// {
// Lock lock = LockE(&NET_W32.pipes_mutex);
// {
// }
// Unlock(&lock);
// }
//////////////////////////////
//- Receive messages
//////////////////////////////
//- Send messages
// {
// for (NET_W32_Pipe *pipe = first_pipe; pipe; pipe = pipe->next)
// {
// if (pipe->udp)
// {
// sendto(
// pipe->udp,
// packet.text,
// packet.len,
// );
// }
// }
// }
EndScratch(scratch);
}
}

View File

@ -0,0 +1,28 @@
////////////////////////////////////////////////////////////
//~ Pipe types
Struct(NET_W32_Pipe)
{
u64 bound_port;
SOCKET udp;
};
////////////////////////////////////////////////////////////
//~ State types
Struct(NET_W32_Ctx)
{
i32 _;
};
extern NET_W32_Ctx NET_W32;
////////////////////////////////////////////////////////////
//~ Helpers
NET_W32_Pipe *NET_W32_PipeFromHandle(NET_PipeHandle pipe_handle);
////////////////////////////////////////////////////////////
//~ Net worker
void NET_W32_TickForever(WaveLaneCtx *lane);

View File

@ -0,0 +1,11 @@
@Layer net_win32
//////////////////////////////
//- Api
@IncludeC net_win32.h
//////////////////////////////
//- Impl
@IncludeC net_win32.c

318
src/net_old/net_old.h Normal file
View File

@ -0,0 +1,318 @@
////////////////////////////////////////////////////////////
//~ Channel ID
Struct(N_ChannelId)
{
u32 gen;
u32 idx;
};
////////////////////////////////////////////////////////////
//~ Host command types
Enum(N_CmdKind)
{
N_CmdKind_None,
N_CmdKind_TryConnect,
N_CmdKind_ConnectSuccess,
N_CmdKind_Disconnect,
N_CmdKind_Heartbeat,
N_CmdKind_Write
};
Enum(N_WriteFlag)
{
N_WriteFlag_None = 0,
N_WriteFlag_Reliable = (1 << 0)
};
Struct(N_Cmd)
{
N_CmdKind kind;
N_ChannelId channel_id;
u16 heartbeat_id;
u16 heartbeat_ack_id;
b32 write_reliable;
String write_msg;
N_Cmd *next;
};
////////////////////////////////////////////////////////////
//~ Event types
Enum(N_EventKind)
{
N_EventKind_None,
N_EventKind_ChannelOpened,
N_EventKind_ChannelClosed,
N_EventKind_Msg
};
Struct(N_Event)
{
N_EventKind kind;
N_ChannelId channel_id;
String msg;
N_Event *next;
};
Struct(N_EventList)
{
N_Event *first;
N_Event *last;
};
Struct(N_ChannelLookupBin)
{
struct N_Channel *first;
struct N_Channel *last;
};
////////////////////////////////////////////////////////////
//~ Packet types
#define N_PacketMagic 0xd9e3b8b6
#define N_MaxPacketChunkLen 1024
#define N_MaxPacketLen 1280 // Give enough space for msg chunk + header
Enum(N_PacketKind)
{
N_PacketKind_None,
N_PacketKind_TryConnect,
N_PacketKind_ConnectSuccess,
N_PacketKind_Disconnect,
N_PacketKind_Heartbeat,
N_PacketKind_MsgChunk
};
Enum(N_PacketFlag)
{
N_PacketFlag_None = 0,
N_PacketFlag_Reliable = (1 << 0)
};
Struct(N_SndPacket)
{
N_SndPacket *next;
u64 seq;
u64 data_len;
u8 data[N_MaxPacketLen];
};
Struct(N_RcvPacket)
{
PLT_Sock *sock;
PLT_Address address;
String data;
N_RcvPacket *next;
};
Struct(N_RcvBuffer)
{
Arena *arena;
N_RcvPacket *first_packet;
N_RcvPacket *last_packet;
};
////////////////////////////////////////////////////////////
//~ Channel types
Struct(N_Channel)
{
N_ChannelId id;
b32 valid;
b32 connected;
struct N_Host *host;
N_Channel *next_free;
PLT_Address address;
u64 address_hash;
N_Channel *next_address_hash;
N_Channel *prev_address_hash;
// NOTE: Packets are allocated in host's `arena`
N_SndPacket *first_reliable_packet;
N_SndPacket *last_reliable_packet;
N_SndPacket *first_unreliable_packet;
N_SndPacket *last_unreliable_packet;
u64 num_reliable_packets;
u64 num_unreliable_packets;
// NOTE: Msg assemblers are allocated in host's `arena`
struct N_MsgAssembler *least_recent_msg_assembler;
struct N_MsgAssembler *most_recent_msg_assembler;
u16 last_heartbeat_received_id;
u16 last_heartbeat_acked_id;
i64 last_heartbeat_acked_ns;
i64 last_heartbeat_rtt_ns;
u64 last_sent_msg_id;
u64 their_acked_seq;
u64 our_acked_seq;
u64 last_sent_seq;
i64 last_packet_received_ns;
};
Struct(N_ChannelNode)
{
N_Channel *channel;
N_ChannelNode *next;
};
Struct(N_ChannelList)
{
N_ChannelNode *first;
N_ChannelNode *last;
};
////////////////////////////////////////////////////////////
//~ Message asssembler types
Struct(N_MsgAssembler)
{
N_Channel *channel;
b32 is_reliable;
// Free list
N_MsgAssembler *next_free;
// Bucket list
N_MsgAssembler *next_hash;
N_MsgAssembler *prev_hash;
// Channel list
N_MsgAssembler *less_recent;
N_MsgAssembler *more_recent;
u64 msg_id;
u64 hash;
u64 last_chunk_len;
u64 num_chunks_total;
u64 num_chunks_received;
i64 touched_ns;
BuddyBlock *buddy_block;
u8 *chunk_bitmap;
u8 *chunk_data;
};
Struct(N_MsgAssemblerLookupBin)
{
N_MsgAssembler *first;
N_MsgAssembler *last;
};
////////////////////////////////////////////////////////////
//~ Host types
#define N_NumChannelLookupBins 512
#define N_NumMsgAssemblerLookupBins 16384
Struct(N_Host)
{
Arena *arena;
PLT_Sock *sock;
BuddyCtx *buddy; // For storing msg assembler data
Arena *cmd_arena;
N_Cmd *first_cmd;
N_Cmd *last_cmd;
N_Cmd *first_free_cmd;
Arena *channel_arena;
N_Channel *channels;
N_Channel *first_free_channel;
u64 num_channels_reserved;
N_SndPacket *first_free_packet; // Acquired in `arena`
N_MsgAssembler *first_free_msg_assembler; // Acquired in `arena`
N_ChannelLookupBin *channel_lookup_bins; // Acquired in `arena`
u64 num_channel_lookup_bins;
N_MsgAssemblerLookupBin *msg_assembler_lookup_bins; // Acquired in `arena`
u64 num_msg_assembler_lookup_bins;
// Double buffer for incoming data
Mutex rcv_buffer_write_mutex;
N_RcvBuffer *rcv_buffer_read;
N_RcvBuffer *rcv_buffer_write;
u64 bytes_received;
u64 bytes_sent;
};
////////////////////////////////////////////////////////////
//~ Nil constants
Readonly Global N_Channel N_nil_channel = { .valid = 0 };
////////////////////////////////////////////////////////////
//~ Host initialization
N_Host *N_AcquireHost(u16 listen_port);
void N_ReleaseHost(N_Host *host);
////////////////////////////////////////////////////////////
//~ Channel
#define N_NilChannelId (N_ChannelId) { .gen = 0, .idx = 0 }
#define N_AllChannelsId (N_ChannelId) { .gen = U32Max, .idx = U32Max }
#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(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, PLT_Address address);
void N_ReleaseChannel(N_Channel *channel);
////////////////////////////////////////////////////////////
//~ Message assembler
u64 N_HashFromMsg(N_ChannelId channel_id, u64 msg_id);
N_MsgAssembler *N_MsgAssemblerFromMsg(N_Host *host, N_ChannelId channel_id, u64 msg_id);
N_MsgAssembler *N_AcquireMsgAssembler(N_Channel *channel, u64 msg_id, u64 chunk_count, u64 now_ns, b32 is_reliable);
void N_ReleaseMessageAssembler(N_MsgAssembler *ma);
void N_TouchMessageAssembler(N_MsgAssembler *ma, i64 now_ns);
b32 N_IsChunkFilled(N_MsgAssembler *ma, u64 chunk_id);
void N_MarkChunkReceived(N_MsgAssembler *ma, u64 chunk_id);
////////////////////////////////////////////////////////////
//~ Packet
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, 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);
////////////////////////////////////////////////////////////
//~ Channel info
i64 N_GetChannelLastRttNs(N_Host *host, N_ChannelId channel_id);
////////////////////////////////////////////////////////////
//~ Update
N_Event *N_PushEvent(Arena *arena, N_EventList *list);
N_EventList N_BeginUpdate(Arena *arena, N_Host *host);
void N_EndUpdate(N_Host *host);

16
src/net_old/net_old.lay Normal file
View File

@ -0,0 +1,16 @@
@Layer net_old
//////////////////////////////
//- Dependencies
@Dep platform
//////////////////////////////
//- Api
@IncludeC net.h
//////////////////////////////
//- Impl
@IncludeC net.c

View File

@ -3,7 +3,7 @@ ThreadLocal P_ThreadLocalCtx P_tl = Zi;
Readonly P_Ent P_NilEnt = { Readonly P_Ent P_NilEnt = {
.xf = CompXformIdentity, .xf = CompXformIdentity,
.look = { 0, -1 }, .control.look = { 0, -1 },
}; };
Readonly P_Frame P_NilFrame = { Readonly P_Frame P_NilFrame = {
@ -1172,7 +1172,13 @@ void P_DebugDrawShape(P_Shape shape, Vec4 srgb)
P_Msg *P_PushMsg(P_MsgKind kind, String data) P_Msg *P_PushMsg(P_MsgKind kind, String data)
{ {
// TODO P_MsgNode *msg_node = PushStruct(P_tl.out_msgs_arena, P_MsgNode);
P_Msg *msg = &msg_node->msg;
msg->kind = kind;
msg->data = PushString(P_tl.out_msgs_arena, data);
DllQueuePush(P_tl.out_msgs.first, P_tl.out_msgs.last, msg_node);
++P_tl.out_msgs.count;
return msg;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -1405,7 +1411,7 @@ void P_StepFrame(P_Frame *frame)
// for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) // for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// { // {
// if (ent->fire_held) // if (ent->control.fire_held)
// { // {
// if (ent->has_weapon) // if (ent->has_weapon)
// { // {
@ -1422,12 +1428,12 @@ void P_StepFrame(P_Frame *frame)
{ {
// Xform xf = ent->xf; // Xform xf = ent->xf;
// Xform desired_xf = xf; // Xform desired_xf = xf;
// if (!IsVec2Zero(ent->look)) // if (!IsVec2Zero(ent->control.look))
// { // {
// desired_xf = XformWithWorldRotation(xf, AngleFromVec2(ent->look)); // desired_xf = XformWithWorldRotation(xf, AngleFromVec2(ent->control.look));
// } // }
// f32 move_speed = TweakFloat("Player move speed", 6.5, 0, 20); // f32 move_speed = TweakFloat("Player move speed", 6.5, 0, 20);
// desired_xf.og = AddVec2(xf.og, MulVec2(ent->move, move_speed * sim_dt)); // desired_xf.og = AddVec2(xf.og, MulVec2(ent->control.move, move_speed * sim_dt));
// Vec2 pos_diff = SubVec2(desired_xf.og, xf.og); // Vec2 pos_diff = SubVec2(desired_xf.og, xf.og);
// f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf)); // f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf));
@ -1456,7 +1462,7 @@ void P_StepFrame(P_Frame *frame)
f32 max_speed = TweakFloat("Player max speed", 10, 0, 20); f32 max_speed = TweakFloat("Player max speed", 10, 0, 20);
Vec2 new_velocity = ent->solved_v; Vec2 new_velocity = ent->solved_v;
new_velocity = AddVec2(new_velocity, MulVec2(ent->move, move_force * sim_dt)); new_velocity = AddVec2(new_velocity, MulVec2(ent->control.move, move_force * sim_dt));
// if (Vec2Len(new_velocity) > max_speed) // if (Vec2Len(new_velocity) > max_speed)
// { // {
@ -1907,7 +1913,7 @@ void P_StepFrame(P_Frame *frame)
P_EntList bullets_to_spawn = Zi; P_EntList bullets_to_spawn = Zi;
for (P_Ent *firer = P_FirstEnt(frame); !P_IsEntNil(firer); firer = P_NextEnt(firer)) for (P_Ent *firer = P_FirstEnt(frame); !P_IsEntNil(firer); firer = P_NextEnt(firer))
{ {
if (firer->fire_held) if (firer->control.fire_held)
// if (firer->fire_presses) // if (firer->fire_presses)
{ {
// i64 fire_delta_ns = frame->time_ns - firer->last_fire_ns; // i64 fire_delta_ns = frame->time_ns - firer->last_fire_ns;
@ -1930,7 +1936,7 @@ void P_StepFrame(P_Frame *frame)
{ {
P_Shape firer_world_shape = P_WorldShapeFromEnt(firer); P_Shape firer_world_shape = P_WorldShapeFromEnt(firer);
Vec2 pos = P_EdgePointFromShape(firer_world_shape, firer->look); Vec2 pos = P_EdgePointFromShape(firer_world_shape, firer->control.look);
for (i64 bullet_idx = 0; bullet_idx < tick_bullets_count; ++bullet_idx) for (i64 bullet_idx = 0; bullet_idx < tick_bullets_count; ++bullet_idx)
{ {
@ -1942,7 +1948,7 @@ void P_StepFrame(P_Frame *frame)
f32 rand_angle = ((f32)P_RandU64FromEnt(firer) / (f32)0xFFFFFFFFFFFFFFFFull) - 0.5; f32 rand_angle = ((f32)P_RandU64FromEnt(firer) / (f32)0xFFFFFFFFFFFFFFFFull) - 0.5;
f32 speed = tweak_speed * sim_dt; f32 speed = tweak_speed * sim_dt;
f32 angle = AngleFromVec2(firer->look); f32 angle = AngleFromVec2(firer->control.look);
speed += (speed * 0.5) * rand_speed; speed += (speed * 0.5) * rand_speed;
angle += rand_angle * spread; angle += rand_angle * spread;
@ -2068,7 +2074,7 @@ void P_StepFrame(P_Frame *frame)
{ {
Vec4 color = VEC4(0.4, 0.8, 0.4, 1); Vec4 color = VEC4(0.4, 0.8, 0.4, 1);
Vec2 p0 = world_shape.centroid; Vec2 p0 = world_shape.centroid;
Vec2 p1 = P_EdgePointFromShape(world_shape, ent->look); Vec2 p1 = P_EdgePointFromShape(world_shape, ent->control.look);
P_DebugDrawLine(p0, p1, color); P_DebugDrawLine(p0, p1, color);
} }
} }

View File

@ -69,6 +69,14 @@ Struct(P_DebugDrawNode)
// TODO: Pack efficiently, deduplicate redundant fields // TODO: Pack efficiently, deduplicate redundant fields
Struct(P_Control)
{
Vec2 move;
Vec2 look;
f32 fire_held;
f32 fire_presses;
};
Struct(P_Ent) Struct(P_Ent)
{ {
//- Internal world state //- Internal world state
@ -95,11 +103,6 @@ Struct(P_Ent)
Xform prev_xf; Xform prev_xf;
Xform xf; Xform xf;
Vec2 move;
Vec2 look;
f32 fire_held;
f32 fire_presses;
// TODO: Remove this (weapon testing) // TODO: Remove this (weapon testing)
i64 last_fire_ns; i64 last_fire_ns;
b32 has_weapon; b32 has_weapon;
@ -113,12 +116,17 @@ Struct(P_Ent)
Vec2 hit_entry; Vec2 hit_entry;
Vec2 hit_entry_normal; Vec2 hit_entry_normal;
P_Control control;
//- User //- User
b32 is_user; b32 is_user;
P_Key player; P_Key player;
// FIXME: Ensure this field isn't transmitted in snapshots
NET_Key net;
u8 string_len; u8 string_len;
u8 string_text[32]; u8 string_text[32];
@ -287,8 +295,6 @@ Struct(P_SimSnapshot)
Struct(P_UserSnapshot) Struct(P_UserSnapshot)
{ {
P_Key user;
i64 src_tick;
i64 tick; i64 tick;
Vec2 move; Vec2 move;
@ -305,20 +311,34 @@ Enum(P_MsgKind)
P_MsgKind_None, P_MsgKind_None,
// User -> sim // User -> sim
P_MsgKind_UserSnapshot,
P_MsgKind_SaveWorld, P_MsgKind_SaveWorld,
P_MsgKind_ResetWorld, P_MsgKind_ResetWorld,
P_MsgKind_TileEdit, P_MsgKind_TileEdit,
P_MsgKind_EntEdit, P_MsgKind_EntEdit,
// Sim -> user // Sim -> user
P_MsgKind_SimSnapshot,
P_MsgKind_Tiles, P_MsgKind_Tiles,
}; };
Struct(P_Msg) Struct(P_Msg)
{ {
P_MsgKind kind; //- In
P_Key src_user;
P_Key dst_user; // If dst is 0, then cmd is meant to be broadcast P_Key dst_user; // If dst is 0, then cmd is meant to be broadcast
b32 unreliable;
//- Out
P_Key src_user;
//- In / Out
P_MsgKind kind;
P_UserSnapshot user_snapshot;
P_SimSnapshot sim_snapshot;
P_TileKind tile_kind; P_TileKind tile_kind;
Rng2I32 tile_range; Rng2I32 tile_range;

View File

@ -1,5 +1,10 @@
@Layer pp @Layer pp
//////////////////////////////
//- Dependencies
@Dep net
////////////////////////////// //////////////////////////////
//- Resources //- Resources

View File

@ -4,6 +4,7 @@
//- Dependencies //- Dependencies
@Dep pp @Dep pp
@Dep net
@Dep platform @Dep platform
////////////////////////////// //////////////////////////////

View File

@ -25,6 +25,8 @@ void S_TickForever(WaveLaneCtx *lane)
P_tl.debug_arena = AcquireArena(Gibi(64)); P_tl.debug_arena = AcquireArena(Gibi(64));
P_tl.out_msgs_arena = AcquireArena(Gibi(64)); P_tl.out_msgs_arena = AcquireArena(Gibi(64));
NET_PipeHandle net_pipe = NET_AcquirePipe();
P_World *world = P_AcquireWorld(); P_World *world = P_AcquireWorld();
// TODO: Real per-client deltas // TODO: Real per-client deltas
@ -45,12 +47,6 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Sim loop //- Sim loop
@ -126,104 +122,120 @@ void S_TickForever(WaveLaneCtx *lane)
} }
////////////////////////////// //////////////////////////////
//- Pop messages from user //- Pop messages
u64 desired_port = 22121;
NET_Bind(net_pipe, desired_port);
P_MsgList in_msgs = Zi; P_MsgList in_msgs = Zi;
{ {
LockTicketMutex(&P.u2s_msgs_mutex); NET_MsgList net_msgs = NET_Pop(frame_arena, net_pipe);
{ for (NET_Msg *net_msg = net_msgs.first; net_msg; net_msg = net_msg->next)
for (P_MsgNode *src_msg_node = P.u2s_msgs.first; src_msg_node; src_msg_node = src_msg_node->next)
{ {
String packed = net_msg->data;
P_MsgNode *dst_msg_node = PushStruct(frame_arena, P_MsgNode); P_MsgNode *dst_msg_node = PushStruct(frame_arena, P_MsgNode);
P_Msg *src_msg = &src_msg_node->msg;
P_Msg *dst_msg = &dst_msg_node->msg; P_Msg *dst_msg = &dst_msg_node->msg;
*dst_msg = *src_msg; *dst_msg = P_UnpackMsg(frame_arena, packed);
dst_msg->data = PushString(frame_arena, src_msg->data); // FIXME: Set src user here based on net key
// dst_msg->src_user =
DllQueuePush(in_msgs.first, in_msgs.last, dst_msg_node); DllQueuePush(in_msgs.first, in_msgs.last, dst_msg_node);
++in_msgs.count; ++in_msgs.count;
} }
ResetArena(P.u2s_msgs_arena);
ZeroStruct(&P.u2s_msgs);
}
UnlockTicketMutex(&P.u2s_msgs_mutex);
} }
////////////////////////////// //////////////////////////////
//- Register users //- Process connection messages
// {
// for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next)
// {
// P_Msg *msg = &msg_node->msg;
// //- Register user
// if (msg->kind == P_MsgKind_RegisterUser)
// {
// P_Key user_key = msg->src_user;
// P_Ent *user = P_EntFromKey(world_frame, user_key);
// if (P_EntIsNil(user))
// {
// P_EntList tmp_list = Zi;
// user = P_PushTempEnt(frame_arena, &tmp_list);
// user->key = user_key;
// user->is_user = 1;
// user->exists = 1;
// // FIXME: Set net key here
// i32 min_name_len = 3;
// i32 max_name_len = countof(user->string_text) - 8;
// // De-duplicate user name
// String user_name = msg->data;
// user_name = TrimWhitespace(user_name);
// user_name.len = MinI64(user_name.len, max_name_len);
// user_name = TrimWhitespace(user_name);
// if (user_name.len < min_name_len)
// {
// user_name = Lit("Player");
// }
// {
// String orig_user_name = user_name;
// i64 duplicate_id = 0;
// while (duplicate_id < 1000)
// {
// b32 is_duplicate = 0;
// for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// {
// if (ent->is_user && MatchString(P_StringFromEnt(ent), user_name))
// {
// is_duplicate = 1;
// break;
// }
// }
// if (is_duplicate)
// {
// duplicate_id += 1;
// user_name = StringF(frame_arena, "%F (%F)", FmtString(orig_user_name), FmtSint(duplicate_id));
// }
// else
// {
// break;
// }
// }
// }
// P_SetEntString(user, user_name);
// P_Msg *msg = P_PushMsg(
// P_MsgKind_Chat,
// StringF(
// frame_arena,
// "Player %F connected",
// FmtString(user_name)
// ));
// )
// P_SpawnEntsFromList(world_frame, tmp_list);
// }
// {
// P_Msg *msg = P_PushMsg(P_MsgKind_RegisterSuccess, Zstr);
// msg->dst_user = user->key;
// }
// }
// }
// }
//////////////////////////////
//- Apply user snapshots
{
for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next)
{ {
P_Msg *msg = &msg_node->msg; P_Msg *msg = &msg_node->msg;
P_Key user_key = msg->src_user; if (msg->kind == P_MsgKind_UserSnapshot)
P_Ent *user = P_EntFromKey(world_frame, user_key);
if (P_EntIsNil(user))
{ {
P_EntList tmp_list = Zi;
user = P_PushTempEnt(frame_arena, &tmp_list);
user->key = user_key;
user->is_user = 1;
user->exists = 1;
i32 min_name_len = 3;
i32 max_name_len = countof(user->string_text) - 8;
// De-duplicate user name
String user_name = msg->data;
user_name = TrimWhitespace(user_name);
user_name.len = MinI64(user_name.len, max_name_len);
user_name = TrimWhitespace(user_name);
if (user_name.len < min_name_len)
{
user_name = Lit("Player");
}
{
String orig_user_name = user_name;
i64 duplicate_id = 0;
while (duplicate_id < 1000)
{
b32 is_duplicate = 0;
for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
if (ent->is_user && MatchString(P_StringFromEnt(ent), user_name))
{
is_duplicate = 1;
break;
}
}
if (is_duplicate)
{
duplicate_id += 1;
user_name = StringF(frame_arena, "%F (%F)", FmtString(orig_user_name), FmtSint(duplicate_id));
}
else
{
break;
}
}
}
P_SetEntString(user, user_name);
P_Msg *msg = P_PushMsg(
P_MsgKind_Chat,
StringF(
frame_arena,
"Player %F connected",
FmtString(user_name)
));
)
P_SpawnEntsFromList(world_frame, tmp_list);
}
{
P_Msg *msg = P_PushMsg(P_MsgKind_RegisterSuccess, Zstr);
msg->dst_user = user->key;
}
} }
} }
//////////////////////////////
//- Apply user snapshots
// P_MsgList user_msgs = Zi; // P_MsgList user_msgs = Zi;
// { // {
@ -360,118 +372,208 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Update ent controls //- Update ent controls
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) // for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ // {
ent->fire_presses = 0; // ent->fire_presses = 0;
} // }
for (P_Ent *user = P_FirstEnt(frame); !P_IsEntNil(user); user = P_NextEnt(user)) // for (P_Ent *user = P_FirstEnt(frame); !P_IsEntNil(user); user = P_NextEnt(user))
{ // {
if (user->is_iser) // if (user->is_iser)
{ // {
P_Ent *target = P_EntFromKey(user->player); // P_Ent *target = P_EntFromKey(user->player);
if (!P_IsEntNil(target)) // if (!P_IsEntNil(target))
{ // {
target->move = ClampVec2Len(user->move, 1); // target->move = ClampVec2Len(user->move, 1);
target->look = user->look; // target->look = user->look;
target->fire_held = user->fire_held; // target->fire_held = user->fire_held;
target->fire_presses += user->fire_presses; // target->fire_presses += user->fire_presses;
} // }
} // }
} // }
////////////////////////////// //////////////////////////////
//- Step frame //- Step frame
{ // {
P_StepFrame(world_frame, user_msgs); // P_StepFrame(world_frame, user_msgs);
} // }
////////////////////////////// //////////////////////////////
//- Publish sim state //- Push tile messages
// if (tiles_dirty)
// {
// P_Msg *msg = 0;
// {
// P_MsgNode *msg_node = PushStruct(frame_arena, P_MsgNode);
// DllQueuePush(output->msgs.first, output->msgs.last, msg_node);
// ++output->msgs.count;
// msg = &msg_node->msg;
// }
// msg->kind = P_MsgKind_Tiles;
// msg->tiles_hash = world->tiles_hash;
// msg->raw_tiles = PushStructsNoZero(frame_arena, u8, P_TilesCount);
// msg->tile_range = RNG2I32(VEC2I32(0, 0), VEC2I32(P_TilesPitch, P_TilesPitch));
// CopyBytes(msg->raw_tiles, world->tiles, P_TilesCount);
// has_sent_initial_tick = 1;
// }
//////////////////////////////
//- Push snapshot messages
// for (P_Ent *user = P_FirstEnt(world_frame); !P_IsEntNil(user); user = P_NextEnt(user))
// {
// if (user->is_user)
// {
// Arena *msgs_arena = P_tl.out_msgs_arena;
// P_Msg *msg = P_PushMsg(P_MsgKind_SimSnapshot, Zstr);
// msg->dst_user = user->key;
// msg->unreliable = 1;
// P_SimSnapshot *snapshot = &msg->sim_snapshot;
// {
// snapshot->world_seed = world->seed;
// snapshot->tick = world_frame->tick;
// snapshot->time_ns = world_frame->time_ns;
// //- Push entity deltas
// for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// {
// P_Delta *delta = 0;
// {
// P_DeltaNode *dn = PushStruct(frame_arena, P_DeltaNode);
// snapshot->deltas_count += 1;
// SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn);
// delta = &dn->delta;
// }
// delta->kind = P_DeltaKind_RawEnt;
// delta->ent = *ent;
// }
// //- Push debug draw information
// {
// i64 dst_idx = 0;
// snapshot->first_debug_draw_node = 0;
// snapshot->last_debug_draw_node = 0;
// snapshot->debug_draw_nodes_count = P_tl.debug_draw_nodes_count;
// P_DebugDrawNode *dst_nodes = PushStructsNoZero(frame_arena, P_DebugDrawNode, snapshot->debug_draw_nodes_count);
// for (P_DebugDrawNode *src = P_tl.first_debug_draw_node; src; src = src->next)
// {
// P_DebugDrawNode *dst = &dst_nodes[dst_idx];
// *dst = *src;
// dst_idx += 1;
// SllQueuePush(snapshot->first_debug_draw_node, snapshot->last_debug_draw_node, dst);
// }
// ResetArena(P_tl.debug_arena);
// P_tl.first_debug_draw_node = 0;
// P_tl.last_debug_draw_node = 0;
// P_tl.debug_draw_nodes_count = 0;
// }
// }
// }
// }
//////////////////////////////
//- Send messages
for (P_MsgNode *msg_node = P_tl.out_msgs.first; msg_node; msg_node = msg_node->next)
{
P_Msg *msg = &msg_node->msg;
if (P_IsKeyNil(msg->dst_user))
{
// Broadcast
String packed = P_PackMessage(frame_arena, msg);
for (P_Ent *user = P_FirstEnt(world_frame); !P_IsEntNil(user); user = P_NextEnt(user)) for (P_Ent *user = P_FirstEnt(world_frame); !P_IsEntNil(user); user = P_NextEnt(user))
{ {
if (user->is_user) if (user->is_user)
{ {
P_Frame *src_frame = P_FrameFromTick(world, user->acked_tick); NET_Push(net_pipe, user->net, packed, msg->unreliable);
if (user->is_local) }
}
}
else
{ {
P_Ent *user = P_EntFromKey(world_frame, msg->dst_user);
if (!NET_IsKeyNil(user->net))
{
String packed = P_PackMessage(frame_arena, msg);
NET_Push(net_pipe, user->net, packed, msg->unreliable);
} }
} }
} }
// TODO: Only copy active entities
LockTicketMutex(&P.sim_output_back_tm);
{
P_OutputState *output = &P.sim_output_states[P.sim_output_back_idx];
// Build messages
{
// Push tiles
if (tiles_dirty)
{
P_Msg *msg = 0;
{
P_MsgNode *msg_node = PushStruct(output->arena, P_MsgNode);
DllQueuePush(output->msgs.first, output->msgs.last, msg_node);
++output->msgs.count;
msg = &msg_node->msg;
}
msg->kind = P_MsgKind_Tiles;
msg->tiles_hash = world->tiles_hash;
msg->raw_tiles = PushStructsNoZero(output->arena, u8, P_TilesCount);
msg->tile_range = RNG2I32(VEC2I32(0, 0), VEC2I32(P_TilesPitch, P_TilesPitch));
CopyBytes(msg->raw_tiles, world->tiles, P_TilesCount);
has_sent_initial_tick = 1;
}
}
// Build snapshot
{
P_Snapshot *snapshot = &output->snapshot;
snapshot->world_seed = world->seed; // // TODO: Only copy active entities
snapshot->tick = world_frame->tick; // LockTicketMutex(&P.sim_output_back_tm);
snapshot->time_ns = world_frame->time_ns; // {
// P_OutputState *output = &P.sim_output_states[P.sim_output_back_idx];
// Push entity deltas // // Build messages
for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) // {
{ // // Push tiles
P_Delta *delta = 0; // if (tiles_dirty)
{ // {
P_DeltaNode *dn = PushStruct(output->arena, P_DeltaNode); // P_Msg *msg = 0;
snapshot->deltas_count += 1; // {
SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn); // P_MsgNode *msg_node = PushStruct(output->arena, P_MsgNode);
delta = &dn->delta; // DllQueuePush(output->msgs.first, output->msgs.last, msg_node);
} // ++output->msgs.count;
delta->kind = P_DeltaKind_RawEnt; // msg = &msg_node->msg;
delta->ent = *ent; // }
} // msg->kind = P_MsgKind_Tiles;
// msg->tiles_hash = world->tiles_hash;
// msg->raw_tiles = PushStructsNoZero(output->arena, u8, P_TilesCount);
// msg->tile_range = RNG2I32(VEC2I32(0, 0), VEC2I32(P_TilesPitch, P_TilesPitch));
// CopyBytes(msg->raw_tiles, world->tiles, P_TilesCount);
// has_sent_initial_tick = 1;
// }
// }
// Push debug draw information // // Build snapshot
{ // {
i64 dst_idx = 0; // P_Snapshot *snapshot = &output->snapshot;
snapshot->first_debug_draw_node = 0;
snapshot->last_debug_draw_node = 0;
snapshot->debug_draw_nodes_count = P_tl.debug_draw_nodes_count;
P_DebugDrawNode *dst_nodes = PushStructsNoZero(output->arena, P_DebugDrawNode, snapshot->debug_draw_nodes_count);
for (P_DebugDrawNode *src = P_tl.first_debug_draw_node; src; src = src->next)
{
P_DebugDrawNode *dst = &dst_nodes[dst_idx];
*dst = *src;
dst_idx += 1;
SllQueuePush(snapshot->first_debug_draw_node, snapshot->last_debug_draw_node, dst);
}
ResetArena(P_tl.debug_arena); // snapshot->world_seed = world->seed;
P_tl.first_debug_draw_node = 0; // snapshot->tick = world_frame->tick;
P_tl.last_debug_draw_node = 0; // snapshot->time_ns = world_frame->time_ns;
P_tl.debug_draw_nodes_count = 0;
} // // Push entity deltas
} // for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
} // {
UnlockTicketMutex(&P.sim_output_back_tm); // P_Delta *delta = 0;
// {
// P_DeltaNode *dn = PushStruct(output->arena, P_DeltaNode);
// snapshot->deltas_count += 1;
// SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn);
// delta = &dn->delta;
// }
// delta->kind = P_DeltaKind_RawEnt;
// delta->ent = *ent;
// }
// // Push debug draw information
// {
// i64 dst_idx = 0;
// snapshot->first_debug_draw_node = 0;
// snapshot->last_debug_draw_node = 0;
// snapshot->debug_draw_nodes_count = P_tl.debug_draw_nodes_count;
// P_DebugDrawNode *dst_nodes = PushStructsNoZero(output->arena, P_DebugDrawNode, snapshot->debug_draw_nodes_count);
// for (P_DebugDrawNode *src = P_tl.first_debug_draw_node; src; src = src->next)
// {
// P_DebugDrawNode *dst = &dst_nodes[dst_idx];
// *dst = *src;
// dst_idx += 1;
// SllQueuePush(snapshot->first_debug_draw_node, snapshot->last_debug_draw_node, dst);
// }
// ResetArena(P_tl.debug_arena);
// P_tl.first_debug_draw_node = 0;
// P_tl.last_debug_draw_node = 0;
// P_tl.debug_draw_nodes_count = 0;
// }
// }
// }
// UnlockTicketMutex(&P.sim_output_back_tm);
////////////////////////////// //////////////////////////////
//- Prune ents //- Prune ents
@ -503,13 +605,13 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- End sim frame //- End sim frame
// Reset front input state // // Reset front input state
{ // {
Arena *arena = input->arena; // Arena *arena = input->arena;
ResetArena(arena); // ResetArena(arena);
ZeroStruct(input); // ZeroStruct(input);
input->arena = arena; // input->arena = arena;
} // }
i64 frame_end_ns = TimeNs(); i64 frame_end_ns = TimeNs();

View File

@ -1,5 +1,5 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Transcode //~ World
String P_PackWorld(Arena *arena, P_World *src_world) String P_PackWorld(Arena *arena, P_World *src_world)
{ {
@ -50,7 +50,7 @@ String P_PackWorld(Arena *arena, P_World *src_world)
result.len += StringF(arena, " pos: \"%F\"\n", FmtFloat2(ent->xf.og)).len; result.len += StringF(arena, " pos: \"%F\"\n", FmtFloat2(ent->xf.og)).len;
result.len += StringF(arena, " rot: \"%F\"\n", FmtFloat2(RightFromXform(ent->xf))).len; result.len += StringF(arena, " rot: \"%F\"\n", FmtFloat2(RightFromXform(ent->xf))).len;
result.len += StringF(arena, " exists: \"%F\"\n", FmtFloat(ent->exists)).len; result.len += StringF(arena, " exists: \"%F\"\n", FmtFloat(ent->exists)).len;
result.len += StringF(arena, " look: \"%F\"\n", FmtFloat2(ent->look)).len; result.len += StringF(arena, " look: \"%F\"\n", FmtFloat2(ent->control.look)).len;
} }
result.len += PushString(arena, Lit(" }\n")).len; result.len += PushString(arena, Lit(" }\n")).len;
} }
@ -164,7 +164,7 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
if (MatchString(attr->name, Lit("look"))) if (MatchString(attr->name, Lit("look")))
{ {
Vec2 look = CR_Vec2FromString(attr->value); Vec2 look = CR_Vec2FromString(attr->value);
ent->look = look; ent->control.look = look;
} }
} }
} }
@ -194,3 +194,20 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
EndScratch(scratch); EndScratch(scratch);
return result; return result;
}; };
////////////////////////////////////////////////////////////
//~ Message
String P_PackMessage(Arena *arena, P_Msg *msg)
{
String result = Zi;
// TODO
return result;
}
P_Msg P_UnpackMsg(Arena *arena, String packed)
{
P_Msg result = Zi;
// TODO
return result;
}

View File

@ -24,7 +24,13 @@ Struct(P_UnpackedWorld)
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Transcode //~ World
String P_PackWorld(Arena *arena, P_World *src_world); String P_PackWorld(Arena *arena, P_World *src_world);
P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed); P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed);
////////////////////////////////////////////////////////////
//~ Message
String P_PackMessage(Arena *arena, P_Msg *msg);
P_Msg P_UnpackMsg(Arena *arena, String packed);

View File

@ -4,6 +4,7 @@
//- Dependencies //- Dependencies
@Dep pp @Dep pp
@Dep net
@Dep sprite @Dep sprite
@Dep gpu @Dep gpu
@Dep glyph_cache @Dep glyph_cache

View File

@ -348,8 +348,9 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Init vis state //- Init vis state
Arena *sim_snapshot_arena = AcquireArena(Gibi(64)); Arena *sim_debug_arena = AcquireArena(Gibi(64));
P_SimSnapshot sim_snapshot = Zi; P_DebugDrawNode *first_sim_debug_draw_node = 0;
P_DebugDrawNode *last_sim_debug_draw_node = 0;
P_World *sim_world = P_AcquireWorld(); P_World *sim_world = P_AcquireWorld();
P_World *predict_world = P_AcquireWorld(); P_World *predict_world = P_AcquireWorld();
@ -358,6 +359,8 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch); Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch);
Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch); Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch);
NET_PipeHandle net_pipe = NET_AcquirePipe();
// Init gpu state // Init gpu state
G_ResourceHandle gpu_state = Zi; G_ResourceHandle gpu_state = Zi;
G_ResourceHandle gpu_tiles = Zi; G_ResourceHandle gpu_tiles = Zi;
@ -2622,78 +2625,85 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Pop messages from sim //- Pop messages from sim
// FIXME: Reject messages if not from currently connected sim
P_MsgList in_msgs = Zi; P_MsgList in_msgs = Zi;
{ {
LockTicketMutex(&P.s2u_msgs_mutex); NET_MsgList net_msgs = NET_Pop(frame->arena, net_pipe);
{ for (NET_Msg *net_msg = net_msgs.first; net_msg; net_msg = net_msg->next)
for (P_MsgNode *src_msg_node = P.s2u_msgs.first; src_msg_node; src_msg_node = src_msg_node->next)
{ {
String packed = net_msg->data;
P_MsgNode *dst_msg_node = PushStruct(frame->arena, P_MsgNode); P_MsgNode *dst_msg_node = PushStruct(frame->arena, P_MsgNode);
P_Msg *src_msg = &src_msg_node->msg;
P_Msg *dst_msg = &dst_msg_node->msg; P_Msg *dst_msg = &dst_msg_node->msg;
*dst_msg = *src_msg; *dst_msg = P_UnpackMsg(frame->arena, packed);
dst_msg->data = PushString(frame->arena, src_msg->data); // FIXME: Set src user here based on net key
// dst_msg->src_user =
DllQueuePush(in_msgs.first, in_msgs.last, dst_msg_node); DllQueuePush(in_msgs.first, in_msgs.last, dst_msg_node);
++in_msgs.count; ++in_msgs.count;
} }
ResetArena(P.s2u_msgs_arena);
ZeroStruct(&P.s2u_msgs);
}
UnlockTicketMutex(&P.s2u_msgs_mutex);
} }
////////////////////////////// //////////////////////////////
//- Pop snapshot from sim //- Pop snapshot from sim
{ // {
LockTicketMutex(&P.s2u_snapshot_mutex); // LockTicketMutex(&P.s2u_snapshot_mutex);
P_SimSnapshot *src_snapshot = &P.s2u_snapshot; // P_SimSnapshot *src_snapshot = &P.s2u_snapshot;
if (src_snapshot->tick > sim_snapshot.tick) // if (src_snapshot->tick > sim_snapshot.tick)
{ // {
ResetArena(sim_snapshot_arena); // ResetArena(sim_snapshot_arena);
ZeroStruct(&sim_snapshot); // ZeroStruct(&sim_snapshot);
sim_snapshot.world_seed = src_snapshot->world_seed; // sim_snapshot.world_seed = src_snapshot->world_seed;
sim_snapshot.src_tick = src_snapshot->src_tick; // sim_snapshot.src_tick = src_snapshot->src_tick;
sim_snapshot.tick = src_snapshot->tick; // sim_snapshot.tick = src_snapshot->tick;
sim_snapshot.time_ns = src_snapshot->time_ns; // sim_snapshot.time_ns = src_snapshot->time_ns;
{ // {
//- Copy deltas // //- Copy deltas
{ // {
P_SimDeltaNode *dst_nodes = PushStructsNoZero(sim_snapshot_arena, P_SimDeltaNode, src_snapshot->deltas_count); // P_SimDeltaNode *dst_nodes = PushStructsNoZero(sim_snapshot_arena, P_SimDeltaNode, src_snapshot->deltas_count);
for (P_SimDeltaNode *src_delta_node = src_snapshot->first_delta_node; src_delta_node; src_delta_node = src_delta_node->next) // for (P_SimDeltaNode *src_delta_node = src_snapshot->first_delta_node; src_delta_node; src_delta_node = src_delta_node->next)
{ // {
P_SimDeltaNode *dst_delta_node = &dst_nodes[sim_snapshot.deltas_count]; // P_SimDeltaNode *dst_delta_node = &dst_nodes[sim_snapshot.deltas_count];
*dst_delta_node = *src_delta_node; // *dst_delta_node = *src_delta_node;
SllQueuePush(sim_snapshot.first_delta_node, sim_snapshot.last_delta_node, dst_delta_node); // SllQueuePush(sim_snapshot.first_delta_node, sim_snapshot.last_delta_node, dst_delta_node);
sim_snapshot.deltas_count += 1; // sim_snapshot.deltas_count += 1;
} // }
} // }
//- Copy debug info // //- Copy debug info
{ // {
P_DebugDrawNode *dst_nodes = PushStructsNoZero(sim_snapshot_arena, P_DebugDrawNode, src_snapshot->debug_draw_nodes_count); // P_DebugDrawNode *dst_nodes = PushStructsNoZero(sim_snapshot_arena, P_DebugDrawNode, src_snapshot->debug_draw_nodes_count);
for (P_DebugDrawNode *src = src_snapshot->first_debug_draw_node; src; src = src->next) // for (P_DebugDrawNode *src = src_snapshot->first_debug_draw_node; src; src = src->next)
{ // {
P_DebugDrawNode *dst = &dst_nodes[sim_snapshot.debug_draw_nodes_count]; // P_DebugDrawNode *dst = &dst_nodes[sim_snapshot.debug_draw_nodes_count];
*dst = *src; // *dst = *src;
SllQueuePush(sim_snapshot.first_debug_draw_node, sim_snapshot.last_debug_draw_node, dst); // SllQueuePush(sim_snapshot.first_debug_draw_node, sim_snapshot.last_debug_draw_node, dst);
sim_snapshot.debug_draw_nodes_count += 1; // sim_snapshot.debug_draw_nodes_count += 1;
} // }
} // }
} // }
ResetArena(P.s2u_snapshot_arena); // ResetArena(P.s2u_snapshot_arena);
ZeroStruct(&P.s2u_snapshot); // ZeroStruct(&P.s2u_snapshot);
} // }
UnlockTicketMutex(&P.s2u_snapshot_mutex); // UnlockTicketMutex(&P.s2u_snapshot_mutex);
} // }
////////////////////////////// //////////////////////////////
//- Apply sim msgs //- Apply sim msgs
P_Msg *newest_snapshot_msg = 0;
{ {
for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next) for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next)
{ {
P_Msg *msg = &msg_node->msg; P_Msg *msg = &msg_node->msg;
if (msg->kind == P_MsgKind_SimSnapshot && msg->sim_snapshot.tick > sim_world->last_frame->tick)
{
if (!newest_snapshot_msg || msg->sim_snapshot.tick > newest_snapshot_msg->sim_snapshot.tick)
{
newest_snapshot_msg = msg;
}
}
//- Chat //- Chat
// if (msg->kind == P_MsgKind_Chat) // if (msg->kind == P_MsgKind_Chat)
// { // {
@ -2729,50 +2739,52 @@ void V_TickForever(WaveLaneCtx *lane)
} }
////////////////////////////// //////////////////////////////
//- Apply sim snapshots //- Apply sim snapshot
// if (sim_snapshot.tick > sim_world->last_frame->tick) if (newest_snapshot_msg)
// { {
// P_Frame *src_frame = P_FrameFromTick(sim_world, snapshot->src_tick); P_SimSnapshot *snapshot = &newest_snapshot_msg->sim_snapshot;
// P_Frame *dst_frame = P_PushFrame(sim_world, src_frame, snapshot->tick);
// sim_world->seed = snapshot->world_seed;
// dst_frame->time_ns = snapshot->time_ns;
// //- Apply deltas P_Frame *src_frame = P_FrameFromTick(sim_world, snapshot->src_tick);
// for (P_SimDeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next) P_Frame *dst_frame = P_PushFrame(sim_world, src_frame, snapshot->tick);
// { sim_world->seed = snapshot->world_seed;
// P_SimDelta *delta = &dn->delta; dst_frame->time_ns = snapshot->time_ns;
// //- Raw ent
// if (delta->kind == P_SimDeltaKind_RawEnt)
// {
// P_Ent tmp_ent = delta->ent;
// P_EntListNode tmp_ent_node = Zi;
// tmp_ent_node.ent = tmp_ent;
// P_EntList ent_list = Zi;
// ent_list.first = &tmp_ent_node;
// ent_list.last = &tmp_ent_node;
// P_SpawnEntsFromList(dst_frame, ent_list);
// }
// }
// //- Update sim debug info //- Apply deltas
// { for (P_SimDeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
// ResetArena(sim_debug_arena); {
// first_sim_debug_draw_node = 0; P_SimDelta *delta = &dn->delta;
// last_sim_debug_draw_node = 0; //- Raw ent
// { if (delta->kind == P_SimDeltaKind_RawEnt)
// i64 dst_idx = 0; {
// P_DebugDrawNode *dst_nodes = PushStructsNoZero(sim_debug_arena, P_DebugDrawNode, snapshot->debug_draw_nodes_count); P_Ent tmp_ent = delta->ent;
// for (P_DebugDrawNode *src = snapshot->first_debug_draw_node; src; src = src->next) P_EntListNode tmp_ent_node = Zi;
// { tmp_ent_node.ent = tmp_ent;
// P_DebugDrawNode *dst = &dst_nodes[dst_idx]; P_EntList ent_list = Zi;
// *dst = *src; ent_list.first = &tmp_ent_node;
// dst_idx += 1; ent_list.last = &tmp_ent_node;
// SllQueuePush(first_sim_debug_draw_node, last_sim_debug_draw_node, dst); P_SpawnEntsFromList(dst_frame, ent_list);
// } }
// } }
// }
// } //- Update sim debug info
{
ResetArena(sim_debug_arena);
first_sim_debug_draw_node = 0;
last_sim_debug_draw_node = 0;
{
i64 dst_idx = 0;
P_DebugDrawNode *dst_nodes = PushStructsNoZero(sim_debug_arena, P_DebugDrawNode, snapshot->debug_draw_nodes_count);
for (P_DebugDrawNode *src = snapshot->first_debug_draw_node; src; src = src->next)
{
P_DebugDrawNode *dst = &dst_nodes[dst_idx];
*dst = *src;
dst_idx += 1;
SllQueuePush(first_sim_debug_draw_node, last_sim_debug_draw_node, dst);
}
}
}
}
// TODO: Remove this // TODO: Remove this
P_ClearFrames(sim_world, I64Min, sim_world->last_frame->tick - 1); P_ClearFrames(sim_world, I64Min, sim_world->last_frame->tick - 1);
@ -2815,7 +2827,7 @@ void V_TickForever(WaveLaneCtx *lane)
} }
////////////////////////////// //////////////////////////////
//- Push user snapshot //- Push user snapshots
// FIXME: Real ping // FIXME: Real ping
// f64 ping = 0.250; // f64 ping = 0.250;
@ -2823,39 +2835,39 @@ void V_TickForever(WaveLaneCtx *lane)
i64 ping_ns = NsFromSeconds(ping); i64 ping_ns = NsFromSeconds(ping);
frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0); frame->predict_to = sim_world->last_frame->tick + MaxF64(CeilF64(ping * SIM_TICKS_PER_SECOND), 1.0);
if (frame->predict_to != prev_frame->predict_to) // if (frame->predict_to != prev_frame->predict_to)
{ // {
{ // {
P_UserSnapshot snapshot = Zi; // P_UserSnapshot snapshot = Zi;
snapshot->user = V.user_user; // snapshot->user = V.user_user;
snapshot->src_tick = 0; // snapshot->src_tick = 0;
// FIXME: Generate snapshots for all new frames, not just last // // FIXME: Generate snapshots for all new frames, not just last
snapshot->tick = frame->predict_to; // snapshot->tick = frame->predict_to;
snapshot->move = frame->move; // snapshot->move = frame->move;
snapshot->look = frame->look; // snapshot->look = frame->look;
snapshot->fire_held = frame->fire_held; // snapshot->fire_held = frame->fire_held;
snapshot->fire_presses = frame->fire_presses; // snapshot->fire_presses = frame->fire_presses;
} // }
} // }
////////////////////////////// //////////////////////////////
//- Submit sim commands //- Submit sim commands
{ // {
LockTicketMutex(&P.sim_input_back_tm); // LockTicketMutex(&P.sim_input_back_tm);
{ // {
P_InputState *v2s = &P.sim_input_states[P.sim_input_back_idx]; // P_InputState *v2s = &P.sim_input_states[P.sim_input_back_idx];
for (P_MsgNode *src_cmd_node = V.sim_cmds.first; src_cmd_node; src_cmd_node = src_cmd_node->next) // for (P_MsgNode *src_cmd_node = V.sim_cmds.first; src_cmd_node; src_cmd_node = src_cmd_node->next)
{ // {
P_MsgNode *dst_cmd_node = PushStruct(v2s->arena, P_MsgNode); // P_MsgNode *dst_cmd_node = PushStruct(v2s->arena, P_MsgNode);
dst_cmd_node->cmd = src_cmd_node->cmd; // dst_cmd_node->cmd = src_cmd_node->cmd;
dst_cmd_node->cmd.tick = frame->predict_to; // dst_cmd_node->cmd.tick = frame->predict_to;
DllQueuePush(v2s->cmds.first, v2s->cmds.last, dst_cmd_node); // DllQueuePush(v2s->cmds.first, v2s->cmds.last, dst_cmd_node);
++v2s->cmds.count; // ++v2s->cmds.count;
} // }
} // }
UnlockTicketMutex(&P.sim_input_back_tm); // UnlockTicketMutex(&P.sim_input_back_tm);
} // }
////////////////////////////// //////////////////////////////
//- Predict //- Predict
@ -3354,66 +3366,66 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Debug draw //- Debug draw
{ // {
// Merge vis debug draws with sim debug draws // // Merge vis debug draws with sim debug draws
P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node; // P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node;
P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node; // P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node;
if (P_tl.first_debug_draw_node) // if (P_tl.first_debug_draw_node)
{ // {
if (last_debug_draw_node) // if (last_debug_draw_node)
{ // {
last_debug_draw_node->next = P_tl.first_debug_draw_node; // last_debug_draw_node->next = P_tl.first_debug_draw_node;
} // }
else // else
{ // {
first_debug_draw_node = P_tl.first_debug_draw_node; // first_debug_draw_node = P_tl.first_debug_draw_node;
} // }
last_debug_draw_node = P_tl.last_debug_draw_node; // last_debug_draw_node = P_tl.last_debug_draw_node;
} // }
// Push draws // // Push draws
for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next) // for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next)
{ // {
Vec4 color = Vec4FromU32(n->srgb32); // Vec4 color = Vec4FromU32(n->srgb32);
i32 detail = 24; // i32 detail = 24;
f32 radius = 5; // f32 radius = 5;
switch(n->kind) // switch(n->kind)
{ // {
case P_DebugDrawKind_Point: // case P_DebugDrawKind_Point:
{ // {
Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p); // Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p);
V_DrawPoint(ui_p, color); // V_DrawPoint(ui_p, color);
} break; // } break;
case P_DebugDrawKind_Line: // case P_DebugDrawKind_Line:
{ // {
Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0); // Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0);
Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1); // Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1);
V_DrawLine(ui_p0, ui_p1, color); // V_DrawLine(ui_p0, ui_p1, color);
} break; // } break;
case P_DebugDrawKind_Rect: // case P_DebugDrawKind_Rect:
{ // {
Rng2 ui_rect = Zi; // Rng2 ui_rect = Zi;
ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0); // ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0);
ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1); // ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1);
V_DrawRect(ui_rect, color, V_DrawFlag_Line); // V_DrawRect(ui_rect, color, V_DrawFlag_Line);
} break; // } break;
case P_DebugDrawKind_Shape: // case P_DebugDrawKind_Shape:
{ // {
P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape); // P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape);
V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line); // V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
} break; // } break;
} // }
} // }
// Reset vis debug draws // // Reset vis debug draws
ResetArena(P_tl.debug_arena); // ResetArena(P_tl.debug_arena);
P_tl.first_debug_draw_node = 0; // P_tl.first_debug_draw_node = 0;
P_tl.last_debug_draw_node = 0; // P_tl.last_debug_draw_node = 0;
P_tl.debug_draw_nodes_count = 0; // P_tl.debug_draw_nodes_count = 0;
} // }
////////////////////////////// //////////////////////////////
@ -3689,21 +3701,21 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Prune sim cmds //- Prune sim cmds
{ // {
for (P_MsgNode *msg_node = V.sim_cmds.first; cmd_node;) // for (P_MsgNode *msg_node = V.sim_cmds.first; cmd_node;)
{ // {
P_MsgNode *next = cmd_node->next; // P_MsgNode *next = cmd_node->next;
{ // {
if (!cmd_node->cmd.predicted || cmd_node->cmd.tick <= sim_world->last_frame->tick) // if (!cmd_node->cmd.predicted || cmd_node->cmd.tick <= sim_world->last_frame->tick)
{ // {
DllQueueRemove(V.sim_cmds.first, V.sim_cmds.last, cmd_node); // DllQueueRemove(V.sim_cmds.first, V.sim_cmds.last, cmd_node);
SllStackPush(V.first_free_sim_cmd_node, cmd_node); // SllStackPush(V.first_free_sim_cmd_node, cmd_node);
--V.sim_cmds.count; // --V.sim_cmds.count;
} // }
} // }
cmd_node = next; // cmd_node = next;
} // }
} // }
////////////////////////////// //////////////////////////////
//- Prune sim ents //- Prune sim ents
@ -3731,17 +3743,27 @@ void V_TickForever(WaveLaneCtx *lane)
} }
} }
//////////////////////////////
//- Send messages
for (P_MsgNode *msg_node = P_tl.out_msgs.first; msg_node; msg_node = msg_node->next)
{
P_Msg *msg = &msg_node->msg;
String packed = P_PackMessage(frame->arena, msg);
NET_Push(net_pipe, user->net, packed, msg->unreliable);
}
////////////////////////////// //////////////////////////////
//- End frame //- End frame
i32 vsync = !!TweakBool("Vsync", 1); i32 vsync = !!TweakBool("Vsync", 1);
{ // {
Arena *old_arena = sim_output->arena; // Arena *old_arena = sim_output->arena;
ZeroStruct(sim_output); // ZeroStruct(sim_output);
sim_output->arena = old_arena; // sim_output->arena = old_arena;
ResetArena(sim_output->arena); // ResetArena(sim_output->arena);
} // }
G_CommitCommandList(frame->cl); G_CommitCommandList(frame->cl);