308 lines
8.6 KiB
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;
|
|
}
|