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 || (
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
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
echo Visual studio development environment activated
echo **************************************************************************
echo.
)
::- Meta build

View File

@ -439,15 +439,6 @@
#define IsIndexable(a) (sizeof(a[0]) != 0)
#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
#define BeginFieldRegion(name) i8 __begfieldreg__##name
#define EndFieldRegion(name) i8 __endfieldreg__##name

View File

@ -6,7 +6,7 @@
#define COBJMACROS
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#pragma warning(push, 0)
// #pragma warning(push, 0)
#include <Windows.h>
#include <combaseapi.h>
#include <dcommon.h>
@ -24,7 +24,7 @@
#include <dwmapi.h>
#include <avrt.h>
#include <shellapi.h>
#pragma warning(pop)
// #pragma warning(pop)
#ifndef BCRYPT_RNG_ALG_HANDLE
#define BCRYPT_RNG_ALG_HANDLE ((void *)0x00000081)

View File

@ -249,9 +249,9 @@ Enum(G_Layout)
{
G_Layout_NoChange,
// Allows a resource to be used on any queue with any access type, as long
// as there is only one writer at a time, and the writer is not writing to
// any texels currently being read.
// "Simultaneous" allows a resource to be used on any queue with any access
// type, as long as there is only one writer at a time, and the writer is not
// writing to any texels currently being read.
// Resources cannot transition to/from this layout. They must be created
// with it and are locked to it.
G_Layout_Simultaneous, // D3D12_BARRIER_LAYOUT_COMMON + D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS
@ -711,7 +711,7 @@ u32 G_PushRef(G_ArenaHandle arena, G_ResourceHandle resource, G_RefDesc desc);
G_CommandListHandle G_PrepareCommandList(G_QueueKind queue);
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_CopyCpuToTexture(G_CommandListHandle cl, G_ResourceHandle dst, Vec3I32 dst_offset, void *src, Vec3I32 src_dims, Rng3I32 src_copy_range);

View File

@ -2429,7 +2429,7 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle)
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)
{
@ -2763,7 +2763,6 @@ G_QueueCompletions G_CompletionTargetsFromQueues(G_QueueMask queue_mask)
void G_SyncEx(G_QueueBarrierDesc desc)
{
u64 fences_count = 0;
ID3D12Fence *fences[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.
// Supposedly amd hardware will start spilling constants once there
// are more than 12: https://gpuopen.com/learn/rdna-performance-guide/
// Supposedly AMD hardware will start spilling constants once there
// are more than 12 - https://gpuopen.com/learn/rdna-performance-guide/
//
#define G_NumGeneralPurposeConstants (8) // Constants available for any usage
#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
// TODO: Non-uniform resource access currently is assumed as the default
// 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> 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");
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;
@ -440,6 +440,8 @@ void BuildEntryPoint(WaveLaneCtx *lane)
//////////////////////////////
//- Phase 1/3: Prep
// Build phases:
//
// Phase 1/3: Prep (narrow)
// - Parse layers
// - Generate final C file

View File

@ -1,318 +1,52 @@
////////////////////////////////////////////////////////////
//~ Channel ID
//~ Opaque types
Struct(N_ChannelId)
{
u32 gen;
u32 idx;
};
Struct(NET_PipeHandle) { u64 v; };
#define NET_IsPipeNil(h) ((h).v == 0)
////////////////////////////////////////////////////////////
//~ Host command types
//~ Key types
Enum(N_CmdKind)
Struct(NET_Key)
{
N_CmdKind_None,
N_CmdKind_TryConnect,
N_CmdKind_ConnectSuccess,
N_CmdKind_Disconnect,
N_CmdKind_Heartbeat,
N_CmdKind_Write
u64 v;
};
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;
};
#define NET_NilKey ((NET_Key) { 0 })
#define NET_IsKeyNil(k) ((k).v == 0)
////////////////////////////////////////////////////////////
//~ Event types
//~ Message types
Enum(N_EventKind)
Struct(NET_Msg)
{
N_EventKind_None,
N_EventKind_ChannelOpened,
N_EventKind_ChannelClosed,
N_EventKind_Msg
};
NET_Msg *next;
NET_Msg *prev;
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;
NET_Key src;
String data;
N_RcvPacket *next;
};
Struct(N_RcvBuffer)
Struct(NET_MsgList)
{
Arena *arena;
N_RcvPacket *first_packet;
N_RcvPacket *last_packet;
i64 count;
NET_Msg *first;
NET_Msg *last;
};
////////////////////////////////////////////////////////////
//~ Channel types
//~ @hookdecl Bootstrap
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;
};
void NET_Bootstrap(void);
////////////////////////////////////////////////////////////
//~ Message asssembler types
//~ @hookdecl Net ops
Struct(N_MsgAssembler)
{
N_Channel *channel;
b32 is_reliable;
NET_PipeHandle NET_AcquirePipe(void);
// Free list
N_MsgAssembler *next_free;
void NET_Bind(NET_PipeHandle pipe, u64 port);
u64 NET_BoundPortFromPipe(NET_PipeHandle pipe_handle);
// 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);
void NET_Push(NET_PipeHandle pipe, NET_Key dst, String data, b32 unreliable);
NET_MsgList NET_Pop(Arena *arena, NET_PipeHandle pipe);

View File

@ -1,16 +1,13 @@
@Layer net
//////////////////////////////
//- Dependencies
@Dep platform
//////////////////////////////
//- Api
@IncludeC net.h
@Bootstrap NET_Bootstrap
//////////////////////////////
//- 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 = {
.xf = CompXformIdentity,
.look = { 0, -1 },
.control.look = { 0, -1 },
};
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)
{
// 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))
// {
// if (ent->fire_held)
// if (ent->control.fire_held)
// {
// if (ent->has_weapon)
// {
@ -1422,12 +1428,12 @@ void P_StepFrame(P_Frame *frame)
{
// Xform xf = ent->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);
// 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);
// 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);
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)
// {
@ -1907,7 +1913,7 @@ void P_StepFrame(P_Frame *frame)
P_EntList bullets_to_spawn = Zi;
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)
{
// 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);
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)
{
@ -1942,7 +1948,7 @@ void P_StepFrame(P_Frame *frame)
f32 rand_angle = ((f32)P_RandU64FromEnt(firer) / (f32)0xFFFFFFFFFFFFFFFFull) - 0.5;
f32 speed = tweak_speed * sim_dt;
f32 angle = AngleFromVec2(firer->look);
f32 angle = AngleFromVec2(firer->control.look);
speed += (speed * 0.5) * rand_speed;
angle += rand_angle * spread;
@ -2068,7 +2074,7 @@ void P_StepFrame(P_Frame *frame)
{
Vec4 color = VEC4(0.4, 0.8, 0.4, 1);
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);
}
}

View File

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

View File

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

View File

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

View File

@ -25,6 +25,8 @@ void S_TickForever(WaveLaneCtx *lane)
P_tl.debug_arena = AcquireArena(Gibi(64));
P_tl.out_msgs_arena = AcquireArena(Gibi(64));
NET_PipeHandle net_pipe = NET_AcquirePipe();
P_World *world = P_AcquireWorld();
// TODO: Real per-client deltas
@ -45,12 +47,6 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- 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;
{
LockTicketMutex(&P.u2s_msgs_mutex);
{
for (P_MsgNode *src_msg_node = P.u2s_msgs.first; src_msg_node; src_msg_node = src_msg_node->next)
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)
{
String packed = net_msg->data;
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;
*dst_msg = *src_msg;
dst_msg->data = PushString(frame_arena, src_msg->data);
*dst_msg = P_UnpackMsg(frame_arena, packed);
// FIXME: Set src user here based on net key
// dst_msg->src_user =
DllQueuePush(in_msgs.first, in_msgs.last, dst_msg_node);
++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)
{
P_Msg *msg = &msg_node->msg;
P_Key user_key = msg->src_user;
P_Ent *user = P_EntFromKey(world_frame, user_key);
if (P_EntIsNil(user))
if (msg->kind == P_MsgKind_UserSnapshot)
{
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;
// {
@ -360,118 +372,208 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Update ent controls
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
ent->fire_presses = 0;
}
// for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
// {
// ent->fire_presses = 0;
// }
for (P_Ent *user = P_FirstEnt(frame); !P_IsEntNil(user); user = P_NextEnt(user))
{
if (user->is_iser)
{
P_Ent *target = P_EntFromKey(user->player);
if (!P_IsEntNil(target))
{
target->move = ClampVec2Len(user->move, 1);
target->look = user->look;
target->fire_held = user->fire_held;
target->fire_presses += user->fire_presses;
}
}
}
// for (P_Ent *user = P_FirstEnt(frame); !P_IsEntNil(user); user = P_NextEnt(user))
// {
// if (user->is_iser)
// {
// P_Ent *target = P_EntFromKey(user->player);
// if (!P_IsEntNil(target))
// {
// target->move = ClampVec2Len(user->move, 1);
// target->look = user->look;
// target->fire_held = user->fire_held;
// target->fire_presses += user->fire_presses;
// }
// }
// }
//////////////////////////////
//- 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))
{
if (user->is_user)
{
P_Frame *src_frame = P_FrameFromTick(world, user->acked_tick);
if (user->is_local)
NET_Push(net_pipe, user->net, packed, msg->unreliable);
}
}
}
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;
snapshot->tick = world_frame->tick;
snapshot->time_ns = world_frame->time_ns;
// // TODO: Only copy active entities
// LockTicketMutex(&P.sim_output_back_tm);
// {
// P_OutputState *output = &P.sim_output_states[P.sim_output_back_idx];
// 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(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;
}
// // 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;
// }
// }
// 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);
}
// // Build snapshot
// {
// P_Snapshot *snapshot = &output->snapshot;
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);
// 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(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
@ -503,13 +605,13 @@ void S_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- End sim frame
// Reset front input state
{
Arena *arena = input->arena;
ResetArena(arena);
ZeroStruct(input);
input->arena = arena;
}
// // Reset front input state
// {
// Arena *arena = input->arena;
// ResetArena(arena);
// ZeroStruct(input);
// input->arena = arena;
// }
i64 frame_end_ns = TimeNs();

View File

@ -1,5 +1,5 @@
////////////////////////////////////////////////////////////
//~ Transcode
//~ 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, " rot: \"%F\"\n", FmtFloat2(RightFromXform(ent->xf))).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;
}
@ -164,7 +164,7 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
if (MatchString(attr->name, Lit("look")))
{
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);
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);
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
@Dep pp
@Dep net
@Dep sprite
@Dep gpu
@Dep glyph_cache

View File

@ -348,8 +348,9 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Init vis state
Arena *sim_snapshot_arena = AcquireArena(Gibi(64));
P_SimSnapshot sim_snapshot = Zi;
Arena *sim_debug_arena = AcquireArena(Gibi(64));
P_DebugDrawNode *first_sim_debug_draw_node = 0;
P_DebugDrawNode *last_sim_debug_draw_node = 0;
P_World *sim_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 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch);
NET_PipeHandle net_pipe = NET_AcquirePipe();
// Init gpu state
G_ResourceHandle gpu_state = Zi;
G_ResourceHandle gpu_tiles = Zi;
@ -2622,78 +2625,85 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Pop messages from sim
// FIXME: Reject messages if not from currently connected sim
P_MsgList in_msgs = Zi;
{
LockTicketMutex(&P.s2u_msgs_mutex);
{
for (P_MsgNode *src_msg_node = P.s2u_msgs.first; src_msg_node; src_msg_node = src_msg_node->next)
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)
{
String packed = net_msg->data;
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;
*dst_msg = *src_msg;
dst_msg->data = PushString(frame->arena, src_msg->data);
*dst_msg = P_UnpackMsg(frame->arena, packed);
// FIXME: Set src user here based on net key
// dst_msg->src_user =
DllQueuePush(in_msgs.first, in_msgs.last, dst_msg_node);
++in_msgs.count;
}
ResetArena(P.s2u_msgs_arena);
ZeroStruct(&P.s2u_msgs);
}
UnlockTicketMutex(&P.s2u_msgs_mutex);
}
//////////////////////////////
//- Pop snapshot from sim
{
LockTicketMutex(&P.s2u_snapshot_mutex);
P_SimSnapshot *src_snapshot = &P.s2u_snapshot;
if (src_snapshot->tick > sim_snapshot.tick)
{
ResetArena(sim_snapshot_arena);
ZeroStruct(&sim_snapshot);
sim_snapshot.world_seed = src_snapshot->world_seed;
sim_snapshot.src_tick = src_snapshot->src_tick;
sim_snapshot.tick = src_snapshot->tick;
sim_snapshot.time_ns = src_snapshot->time_ns;
{
//- Copy deltas
{
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)
{
P_SimDeltaNode *dst_delta_node = &dst_nodes[sim_snapshot.deltas_count];
*dst_delta_node = *src_delta_node;
SllQueuePush(sim_snapshot.first_delta_node, sim_snapshot.last_delta_node, dst_delta_node);
sim_snapshot.deltas_count += 1;
}
}
//- Copy debug info
{
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)
{
P_DebugDrawNode *dst = &dst_nodes[sim_snapshot.debug_draw_nodes_count];
*dst = *src;
SllQueuePush(sim_snapshot.first_debug_draw_node, sim_snapshot.last_debug_draw_node, dst);
sim_snapshot.debug_draw_nodes_count += 1;
}
}
}
ResetArena(P.s2u_snapshot_arena);
ZeroStruct(&P.s2u_snapshot);
}
UnlockTicketMutex(&P.s2u_snapshot_mutex);
}
// {
// LockTicketMutex(&P.s2u_snapshot_mutex);
// P_SimSnapshot *src_snapshot = &P.s2u_snapshot;
// if (src_snapshot->tick > sim_snapshot.tick)
// {
// ResetArena(sim_snapshot_arena);
// ZeroStruct(&sim_snapshot);
// sim_snapshot.world_seed = src_snapshot->world_seed;
// sim_snapshot.src_tick = src_snapshot->src_tick;
// sim_snapshot.tick = src_snapshot->tick;
// sim_snapshot.time_ns = src_snapshot->time_ns;
// {
// //- Copy deltas
// {
// 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)
// {
// P_SimDeltaNode *dst_delta_node = &dst_nodes[sim_snapshot.deltas_count];
// *dst_delta_node = *src_delta_node;
// SllQueuePush(sim_snapshot.first_delta_node, sim_snapshot.last_delta_node, dst_delta_node);
// sim_snapshot.deltas_count += 1;
// }
// }
// //- Copy debug info
// {
// 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)
// {
// P_DebugDrawNode *dst = &dst_nodes[sim_snapshot.debug_draw_nodes_count];
// *dst = *src;
// SllQueuePush(sim_snapshot.first_debug_draw_node, sim_snapshot.last_debug_draw_node, dst);
// sim_snapshot.debug_draw_nodes_count += 1;
// }
// }
// }
// ResetArena(P.s2u_snapshot_arena);
// ZeroStruct(&P.s2u_snapshot);
// }
// UnlockTicketMutex(&P.s2u_snapshot_mutex);
// }
//////////////////////////////
//- Apply sim msgs
P_Msg *newest_snapshot_msg = 0;
{
for (P_MsgNode *msg_node = in_msgs.first; msg_node; msg_node = msg_node->next)
{
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
// 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)
// {
// P_Frame *src_frame = P_FrameFromTick(sim_world, snapshot->src_tick);
// 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;
if (newest_snapshot_msg)
{
P_SimSnapshot *snapshot = &newest_snapshot_msg->sim_snapshot;
// //- Apply deltas
// for (P_SimDeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
// {
// P_SimDelta *delta = &dn->delta;
// //- 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);
// }
// }
P_Frame *src_frame = P_FrameFromTick(sim_world, snapshot->src_tick);
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;
// //- 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);
// }
// }
// }
// }
//- Apply deltas
for (P_SimDeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
{
P_SimDelta *delta = &dn->delta;
//- 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
{
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
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
// f64 ping = 0.250;
@ -2823,39 +2835,39 @@ void V_TickForever(WaveLaneCtx *lane)
i64 ping_ns = NsFromSeconds(ping);
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)
{
{
P_UserSnapshot snapshot = Zi;
snapshot->user = V.user_user;
snapshot->src_tick = 0;
// FIXME: Generate snapshots for all new frames, not just last
snapshot->tick = frame->predict_to;
snapshot->move = frame->move;
snapshot->look = frame->look;
snapshot->fire_held = frame->fire_held;
snapshot->fire_presses = frame->fire_presses;
}
}
// if (frame->predict_to != prev_frame->predict_to)
// {
// {
// P_UserSnapshot snapshot = Zi;
// snapshot->user = V.user_user;
// snapshot->src_tick = 0;
// // FIXME: Generate snapshots for all new frames, not just last
// snapshot->tick = frame->predict_to;
// snapshot->move = frame->move;
// snapshot->look = frame->look;
// snapshot->fire_held = frame->fire_held;
// snapshot->fire_presses = frame->fire_presses;
// }
// }
//////////////////////////////
//- Submit sim commands
{
LockTicketMutex(&P.sim_input_back_tm);
{
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)
{
P_MsgNode *dst_cmd_node = PushStruct(v2s->arena, P_MsgNode);
dst_cmd_node->cmd = src_cmd_node->cmd;
dst_cmd_node->cmd.tick = frame->predict_to;
DllQueuePush(v2s->cmds.first, v2s->cmds.last, dst_cmd_node);
++v2s->cmds.count;
}
}
UnlockTicketMutex(&P.sim_input_back_tm);
}
// {
// LockTicketMutex(&P.sim_input_back_tm);
// {
// 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)
// {
// P_MsgNode *dst_cmd_node = PushStruct(v2s->arena, P_MsgNode);
// dst_cmd_node->cmd = src_cmd_node->cmd;
// dst_cmd_node->cmd.tick = frame->predict_to;
// DllQueuePush(v2s->cmds.first, v2s->cmds.last, dst_cmd_node);
// ++v2s->cmds.count;
// }
// }
// UnlockTicketMutex(&P.sim_input_back_tm);
// }
//////////////////////////////
//- Predict
@ -3354,66 +3366,66 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Debug draw
{
// Merge vis debug draws with sim debug draws
P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node;
P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node;
if (P_tl.first_debug_draw_node)
{
if (last_debug_draw_node)
{
last_debug_draw_node->next = P_tl.first_debug_draw_node;
}
else
{
first_debug_draw_node = P_tl.first_debug_draw_node;
}
last_debug_draw_node = P_tl.last_debug_draw_node;
}
// {
// // Merge vis debug draws with sim debug draws
// P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node;
// P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node;
// if (P_tl.first_debug_draw_node)
// {
// if (last_debug_draw_node)
// {
// last_debug_draw_node->next = P_tl.first_debug_draw_node;
// }
// else
// {
// first_debug_draw_node = P_tl.first_debug_draw_node;
// }
// last_debug_draw_node = P_tl.last_debug_draw_node;
// }
// Push draws
for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next)
{
Vec4 color = Vec4FromU32(n->srgb32);
i32 detail = 24;
f32 radius = 5;
switch(n->kind)
{
case P_DebugDrawKind_Point:
{
Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p);
V_DrawPoint(ui_p, color);
} break;
// // Push draws
// for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next)
// {
// Vec4 color = Vec4FromU32(n->srgb32);
// i32 detail = 24;
// f32 radius = 5;
// switch(n->kind)
// {
// case P_DebugDrawKind_Point:
// {
// Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p);
// V_DrawPoint(ui_p, color);
// } break;
case P_DebugDrawKind_Line:
{
Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0);
Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1);
V_DrawLine(ui_p0, ui_p1, color);
} break;
// case P_DebugDrawKind_Line:
// {
// Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0);
// Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1);
// V_DrawLine(ui_p0, ui_p1, color);
// } break;
case P_DebugDrawKind_Rect:
{
Rng2 ui_rect = Zi;
ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0);
ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1);
V_DrawRect(ui_rect, color, V_DrawFlag_Line);
} break;
// case P_DebugDrawKind_Rect:
// {
// Rng2 ui_rect = Zi;
// ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0);
// ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1);
// V_DrawRect(ui_rect, color, V_DrawFlag_Line);
// } break;
case P_DebugDrawKind_Shape:
{
P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape);
V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
} break;
}
}
// case P_DebugDrawKind_Shape:
// {
// P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape);
// V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
// } break;
// }
// }
// Reset vis debug draws
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;
}
// // Reset vis debug draws
// 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;
// }
//////////////////////////////
@ -3689,21 +3701,21 @@ void V_TickForever(WaveLaneCtx *lane)
//////////////////////////////
//- Prune sim cmds
{
for (P_MsgNode *msg_node = V.sim_cmds.first; cmd_node;)
{
P_MsgNode *next = cmd_node->next;
{
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);
SllStackPush(V.first_free_sim_cmd_node, cmd_node);
--V.sim_cmds.count;
}
}
cmd_node = next;
}
}
// {
// for (P_MsgNode *msg_node = V.sim_cmds.first; cmd_node;)
// {
// P_MsgNode *next = cmd_node->next;
// {
// 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);
// SllStackPush(V.first_free_sim_cmd_node, cmd_node);
// --V.sim_cmds.count;
// }
// }
// cmd_node = next;
// }
// }
//////////////////////////////
//- 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
i32 vsync = !!TweakBool("Vsync", 1);
{
Arena *old_arena = sim_output->arena;
ZeroStruct(sim_output);
sim_output->arena = old_arena;
ResetArena(sim_output->arena);
}
// {
// Arena *old_arena = sim_output->arena;
// ZeroStruct(sim_output);
// sim_output->arena = old_arena;
// ResetArena(sim_output->arena);
// }
G_CommitCommandList(frame->cl);