Backlog/engine/net/src/net.zig

308 lines
8.6 KiB
Zig

const core = @import("core");
const std = @import("std");
const net = @import("net.zig");
const netEngine = @import("netEngine.zig");
pub const EnetTransport = netEngine.EnetTransport;
pub const NetEngine = netEngine.NetEngine;
pub const err = netEngine.net_err;
pub const errs = netEngine.net_errs;
pub const log = netEngine.net_log;
pub const logs = netEngine.net_logs;
pub fn netAllocator() std.mem.Allocator {
return core.get(NetEngine).arenaAllocator();
}
pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.mem.Allocator) !void {
_ = args;
_ = spec;
_ = allocator;
if (core.isUtility()) {
return;
}
const engine = try core.createObject(NetEngine, .{ .can_tick = true });
_ = try engine.initalizeTransport(netEngine.EnetTransport);
}
pub fn shutdown_module(allocator: std.mem.Allocator) void {
core.get(NetEngine).shutdown();
_ = allocator;
}
pub const context = core.EngineObject(NetEngine).get;
pub const Module = core.ModuleDescription{
.name = "net",
.enabledByDefault = false,
};
pub const TransportError = error{
UnknownStatePanic,
BadInit,
UnknownError,
OutOfMemory,
ServerStartFailed,
};
pub fn ip2String(allocator: std.mem.Allocator, ip: u32) ![:0]u8 {
return try std.fmt.allocPrintSentinel(allocator, "{d}.{d}.{d}.{d}", .{
(ip >> 0) & 0xFF,
(ip >> 8) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 24) & 0xFF,
}, 0);
}
pub const TransportRef = netEngine.TransportRef;
pub const TransportInterface = core.MakeInterface("TransportInterfaceVTable", struct {
hostSession: *const fn (*anyopaque, bindInfo: ?[]const []const u8) TransportError!*Session,
endSession: *const fn (*anyopaque, *Session) void,
connect: *const fn (*anyopaque, target: []const u8) TransportError!*Link,
endLink: *const fn (*anyopaque, *Link) void,
sendMessage: *const fn (*anyopaque, *Link, []const u8, bool) TransportError!void,
// deinitialize: *const fn (*anyopaque) void,
pub fn Implement(comptime T: type) @This() {
const Wrap = struct {
// sessions interface
pub fn hostSession(p: *anyopaque, bindInfo: ?[]const []const u8) TransportError!*Session {
const ptr: *T = @ptrCast(@alignCast(p));
return ptr.hostSession(bindInfo) catch return TransportError.ServerStartFailed;
}
pub fn endSession(p: *anyopaque, session: *Session) void {
const ptr: *T = @ptrCast(@alignCast(p));
ptr.endSession(session);
}
// links interface
pub fn connect(p: *anyopaque, target: []const u8) TransportError!*Link {
const ptr: *T = @ptrCast(@alignCast(p));
return ptr.connect(target) catch return TransportError.UnknownStatePanic;
}
pub fn endLink(p: *anyopaque, link: *Link) void {
const ptr: *T = @ptrCast(@alignCast(p));
ptr.endLink(link);
}
pub fn sendMessageLink(p: *anyopaque, link: *Link, data: []const u8, reliable: bool) TransportError!void {
const ptr: *T = @ptrCast(@alignCast(p));
ptr.sendMessageLink(link, data, reliable) catch return TransportError.UnknownStatePanic;
}
// transport management
// pub fn deinitialize(p: *anyopaque) void {
// const ptr: *T = @ptrCast(@alignCast(p));
// ptr.deinitialize();
// }
};
inline for (@typeInfo(Wrap).@"struct".decls) |d| {
if (!@hasDecl(T, d.name)) {
@compileError(@typeName(T) ++ " is missing implementation of func " ++ d.name);
}
}
return .{
.hostSession = Wrap.hostSession,
.endSession = Wrap.endSession,
.connect = Wrap.connect,
.endLink = Wrap.endLink,
.sendMessage = Wrap.sendMessageLink,
};
}
});
const NewLinkCallback = struct {
data: *anyopaque,
func: Function,
pub const Function = *const fn (*anyopaque, *Link) void;
};
pub const Session = struct {
transportName: []const u8 = "Dummy",
transport: ?TransportRef = null,
transportData: ?*anyopaque = null,
allocator: std.mem.Allocator,
skipEndSession: bool = false,
newLinkCallback: std.ArrayListUnmanaged(NewLinkCallback) = .{},
links: std.ArrayListUnmanaged(*Link) = .{},
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
return self;
}
pub fn registerNewLinkCallback(self: *@This(), data: ?*anyopaque, callback: NewLinkCallback) void {
try self.newLinkCallback.append(.{ .data = data, .func = callback });
}
pub fn addLink(self: *@This(), link: *Link) !void {
try self.links.append(netAllocator(), link);
link.session = self;
for (self.newLinkCallback.items) |cb| {
cb.func(cb.data, link);
}
}
pub fn removeLink(self: *@This(), link: *Link) void {
for (self.links.items, 0..) |l, i| {
if (l == link) {
_ = self.links.swapRemove(i);
return;
}
}
}
pub fn destroy(self: *@This()) void {
for (self.links.items) |link| {
link.destroy();
}
if (self.transport) |transport| {
if (!self.skipEndSession) {
transport.vtable.endSession(transport.ptr, self);
}
}
self.newLinkCallback.deinit(self.allocator);
self.allocator.destroy(self);
}
};
// end result:
// Links and sessions should be very low level implementations
//
// for the gameplay framework:
// NetEngine will own the link and sessions, and all gameplay will speak to NetEngine.
// host side flow
//
// initialize transport -> TransportInterface
// host Session -> Session // called by NetEngine
// // session now listens for incomming connections
//
// Transport Implementation calls Session->NewLink();
// onLinkCreated -> Link
//
// Link.sendMessage(bytes []const u8, reliable:bool); // can send messages // if you decide to listen to link, you will recieve [[all]] data
// Link.poll() -> ?[]const u8 // recieve messages from the link? (to be owned by netengine)
//
// client side flow
//
// createLink(hostIp) -> Link
//
//
pub const LinkState = enum {
invalid,
connecting, // first state when the peer connects,
connected, //
};
pub const LinkType = enum {
invalid, //
server, //
client, //
peer,
};
pub const LinkEventType = enum {
connected,
disconnected,
kicked,
};
pub const LinkEvent = struct {
link: *Link,
event: LinkEventType,
};
pub const LinkEventCallbackFn = *const fn (?*anyopaque, LinkEvent) void;
pub const LinkEventDelegate = struct {
func: LinkEventCallbackFn,
ptr: ?*anyopaque,
};
pub const Link = struct {
allocator: std.mem.Allocator,
session: ?*Session = null,
idString: []const u8 = "Uninitialized",
state: LinkState = .invalid,
linkType: LinkType = .invalid,
callbacks: std.ArrayListUnmanaged(LinkEventDelegate) = .{},
id: ?u32 = null,
transportData: ?*anyopaque = null,
transport: ?TransportRef = null,
// closes, and pointer is invalidated
pub fn destroy(self: *@This()) void {
if (self.transport) |t| {
t.vtable.endLink(t.ptr, self);
}
if (self.session) |session| {
session.removeLink(self);
}
self.callbacks.deinit(self.allocator);
self.allocator.destroy(self);
}
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(*@This());
self.* = .{
.allocator = allocator,
};
return self;
}
pub fn sendMessage(self: *@This(), bytes: []const u8, reliable: bool) !void {
if (self.transport) |t| {
try t.vtable.sendMessage(t.ptr, self, bytes, reliable);
}
}
pub fn registerLinkEventCallback(self: *@This(), data: ?*anyopaque, cb: LinkEventCallbackFn) !void {
try self.callbacks.append(.{
.ptr = data,
.cb = cb,
});
}
};
pub fn hostSession(bindInfo: ?[]const []const u8) !*Session {
if (core.get(NetEngine).transport) |t| {
return try t.vtable.hostSession(t.ptr, bindInfo);
}
return error.NoTransportAvailable;
}
pub fn connect(target: []const u8) !*Link {
if (core.get(NetEngine).transport) |t| {
return try t.vtable.connect(t.ptr, target);
}
return error.NoTransportAvailable;
}