Merge branch 'integration' of github.com:peterino2/BacklogEngine into integration
This commit is contained in:
commit
5975ac0585
|
|
@ -27,6 +27,7 @@ const engineDepList = [_][]const u8{
|
|||
"assets",
|
||||
"audio",
|
||||
"core",
|
||||
"net",
|
||||
"papyrus",
|
||||
"platform",
|
||||
"rend",
|
||||
|
|
@ -234,9 +235,9 @@ pub fn addExtraModule(self: *BuildSystem, mod: *std.Build.Module, moduleName: []
|
|||
}
|
||||
|
||||
pub fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
|
||||
if (self.staticBuild) {
|
||||
return;
|
||||
}
|
||||
//if (self.staticBuild) {
|
||||
//return;
|
||||
// }
|
||||
|
||||
inline for (DynamicDepList) |d| {
|
||||
b.installArtifact(b.dependency(
|
||||
|
|
@ -257,6 +258,7 @@ const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } =
|
|||
.{ .dep = "lua", .artifact = "luac" },
|
||||
.{ .dep = "miniaudio", .artifact = "miniaudio_c" },
|
||||
.{ .dep = "zphysics", .artifact = "joltc" },
|
||||
.{ .dep = "enet", .artifact = "enet_c" },
|
||||
.{ .dep = "ozz", .artifact = "ozz_cpp" },
|
||||
};
|
||||
|
||||
|
|
@ -273,6 +275,7 @@ pub const moduleOrder: []const []const u8 = &.{
|
|||
"sys",
|
||||
"assets",
|
||||
"platform",
|
||||
"net",
|
||||
"physics",
|
||||
"audio",
|
||||
"rend",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
.assets = .{ .path = "engine/assets" },
|
||||
.audio = .{ .path = "engine/audio" },
|
||||
.core = .{ .path = "engine/core" },
|
||||
.net = .{ .path = "engine/net" },
|
||||
.papyrus = .{ .path = "engine/papyrus" },
|
||||
.physics = .{ .path = "engine/physics" },
|
||||
.platform = .{ .path = "engine/platform" },
|
||||
|
|
@ -19,6 +20,7 @@
|
|||
.zphysics = .{ .path = "lib/zphysics" },
|
||||
|
||||
.sdl3 = .{ .path = "lib/sdl3" },
|
||||
.enet = .{ .path = "lib/enet" },
|
||||
},
|
||||
.paths = .{
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -63,6 +63,10 @@ pub fn main() !void {
|
|||
\\ }} else {{
|
||||
\\ try Struct.start_module(spec, NwArgs{{}}, allocator);
|
||||
\\ }}
|
||||
\\ if(core.MemoryTracker.MTGet()) |_|
|
||||
\\ {{
|
||||
\\ core.MemoryTracker.MTPrintStatsDelta();
|
||||
\\ }}
|
||||
\\ try shutdownList.append(allocator, Struct.shutdown_module);
|
||||
\\ try shutdownModuleNames.append(allocator, feature);
|
||||
\\ core.engine_logs("module started >>>> " ++ feature ++ " <<<<");
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ const std = @import("std");
|
|||
const memory = core.MemoryTracker;
|
||||
|
||||
const soundEngine = @import("sound_engine.zig");
|
||||
pub const NeonSoundEngine = soundEngine.NeonSoundEngine;
|
||||
pub const SoundEngine = soundEngine.SoundEngine;
|
||||
pub const sound_err = soundEngine.sound_err;
|
||||
pub const sound_errs = soundEngine.sound_errs;
|
||||
pub const sound_log = soundEngine.sound_log;
|
||||
pub const sound_logs = soundEngine.sound_logs;
|
||||
|
||||
pub var gSoundEngine: *NeonSoundEngine = undefined;
|
||||
pub var gSoundEngine: *SoundEngine = undefined;
|
||||
pub var gSoundLoader: *soundEngine.SoundLoader = undefined;
|
||||
|
||||
pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.mem.Allocator) !void {
|
||||
|
|
@ -20,12 +20,13 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me
|
|||
return;
|
||||
}
|
||||
|
||||
gSoundEngine = core.createObject(NeonSoundEngine, .{ .can_tick = true }) catch unreachable;
|
||||
gSoundEngine = core.createObject(SoundEngine, .{ .can_tick = true }) catch unreachable;
|
||||
|
||||
gSoundLoader = allocator.create(soundEngine.SoundLoader) catch unreachable;
|
||||
gSoundLoader.* = soundEngine.SoundLoader.init(gSoundEngine);
|
||||
|
||||
assets.getAssets().registerLoader(gSoundLoader) catch unreachable;
|
||||
_ = try core.fs().installFileBytesMount("embedded:engineTick.wav", @constCast(&engineTickWav), true);
|
||||
// var name = core.MakeName("s_test");
|
||||
// gSoundEngine.loadSound(&name, "content/sounds/engineTick.wav", .{}) catch unreachable;
|
||||
|
||||
|
|
@ -37,7 +38,16 @@ pub fn shutdown_module(allocator: std.mem.Allocator) void {
|
|||
gSoundEngine.shutdown();
|
||||
}
|
||||
|
||||
pub const context = core.EngineObject(SoundEngine).get;
|
||||
|
||||
pub fn loadBuiltinSounds() !void {
|
||||
var name = core.MakeName("engine/s_tick");
|
||||
try context().loadSound(&name, "embedded:engineTick.wav", .{});
|
||||
}
|
||||
|
||||
pub const Module = core.ModuleDescription{
|
||||
.name = "audio",
|
||||
.enabledByDefault = false,
|
||||
};
|
||||
|
||||
const engineTickWav align(8) = @embedFile("embedded/engineTick.wav").*;
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ pub fn sound_errs(comptime fmt: []const u8) void {
|
|||
pub const SoundLoader = struct {
|
||||
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Sound", @This());
|
||||
|
||||
engine: *NeonSoundEngine,
|
||||
engine: *SoundEngine,
|
||||
|
||||
pub fn init(engine: *NeonSoundEngine) @This() {
|
||||
pub fn init(engine: *SoundEngine) @This() {
|
||||
return @This(){
|
||||
.engine = engine,
|
||||
};
|
||||
|
|
@ -64,11 +64,12 @@ fn ma_res(value: anytype) !void {
|
|||
}
|
||||
}
|
||||
// On init, SoundEngine will spawn a
|
||||
pub const NeonSoundEngine = struct {
|
||||
pub const SoundEngine = struct {
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "audio.SoundEngine");
|
||||
|
||||
engine: *ma.ma_engine,
|
||||
sounds: AutoHashMapUnmanaged(u32, *ma.ma_sound),
|
||||
decoders: AutoHashMapUnmanaged(u32, *ma.ma_decoder),
|
||||
allocator: std.mem.Allocator,
|
||||
volume: f32 = 1.0,
|
||||
|
||||
|
|
@ -77,6 +78,7 @@ pub const NeonSoundEngine = struct {
|
|||
self.* = @This(){
|
||||
.engine = allocator.create(ma.ma_engine) catch unreachable,
|
||||
.sounds = .{},
|
||||
.decoders = .{},
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
|
|
@ -101,17 +103,31 @@ pub const NeonSoundEngine = struct {
|
|||
const sound = try self.allocator.create(ma.ma_sound);
|
||||
errdefer self.allocator.destroy(sound);
|
||||
|
||||
const res = ma.ma_sound_init_from_file(self.engine, fileName.ptr, 0, null, null, sound);
|
||||
const engineSoundBytes = try core.fs().loadFile(fileName);
|
||||
const bytes = engineSoundBytes.bytes;
|
||||
|
||||
// Create a decoder from the loaded bytes
|
||||
const decoder = try self.allocator.create(ma.ma_decoder);
|
||||
errdefer self.allocator.destroy(decoder);
|
||||
|
||||
const res_decoder = ma.ma_decoder_init_memory(bytes.ptr, bytes.len, null, decoder);
|
||||
if (res_decoder != ma.MA_SUCCESS) {
|
||||
sound_err("failed to initialize decoder for sound: {s}", .{soundName.utf8()});
|
||||
return error.MiniAudioError;
|
||||
}
|
||||
|
||||
const res = ma.ma_sound_init_from_data_source(self.engine, decoder, 0, null, sound);
|
||||
if (res != ma.MA_SUCCESS) {
|
||||
sound_err("tried loading sound: {s} failed", .{soundName.utf8()});
|
||||
return error.MiniAudioError;
|
||||
}
|
||||
|
||||
try self.sounds.put(self.allocator, soundName.handle(), sound);
|
||||
try self.decoders.put(self.allocator, soundName.handle(), decoder);
|
||||
ma.ma_sound_set_volume(sound, soundParams.volume);
|
||||
}
|
||||
|
||||
pub fn playSound(self: *@This(), soundName: core.Name) !void {
|
||||
pub fn playSound(self: *@This(), soundName: *core.Name) !void {
|
||||
const maybeSound = self.sounds.get(soundName.handle());
|
||||
|
||||
if (maybeSound == null)
|
||||
|
|
@ -142,6 +158,14 @@ pub const NeonSoundEngine = struct {
|
|||
self.allocator.destroy(sound.value_ptr.*);
|
||||
}
|
||||
self.sounds.deinit(self.allocator);
|
||||
|
||||
var decoder_iter = self.decoders.iterator();
|
||||
while (decoder_iter.next()) |decoder| {
|
||||
_ = ma.ma_decoder_uninit(decoder.value_ptr.*);
|
||||
self.allocator.destroy(decoder.value_ptr.*);
|
||||
}
|
||||
self.decoders.deinit(self.allocator);
|
||||
|
||||
ma.ma_engine_uninit(self.engine);
|
||||
self.allocator.destroy(self.engine);
|
||||
self.allocator.destroy(self);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ pub const platform = @import("platform").module;
|
|||
pub const assets = @import("assets").module;
|
||||
pub const rend = @import("rend").module;
|
||||
pub const audio = @import("audio").module;
|
||||
pub const net = @import("net").module;
|
||||
pub const ui = @import("ui").module;
|
||||
pub const papyrus = @import("papyrus").module;
|
||||
pub const physics = @import("physics").module;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ allocationsCount: u32 = 0,
|
|||
totalAllocSize: usize = 0,
|
||||
eventsCount: usize = 0,
|
||||
|
||||
untrackedAllocationsCount: u32 = 0,
|
||||
untrackedAllocationsSize: usize = 0,
|
||||
|
||||
peakAllocations: u32 = 0,
|
||||
peakAllocSize: usize = 0,
|
||||
|
||||
|
|
@ -258,11 +261,33 @@ pub fn deinit(self: *@This()) void {
|
|||
}
|
||||
|
||||
pub fn addUntrackedAllocation(self: *@This(), allocatedSize: usize) void {
|
||||
self.totalAllocSize += allocatedSize;
|
||||
self.lock.lock();
|
||||
self.untrackedAllocationsCount += 1;
|
||||
self.untrackedAllocationsSize += allocatedSize;
|
||||
|
||||
if (self.timeline) |*timeline| {
|
||||
timeline.pushAlloc(self.stackCompactor.?, 0, allocatedSize);
|
||||
}
|
||||
|
||||
self.lock.unlock();
|
||||
}
|
||||
|
||||
pub fn removeUntrackedAllocation(self: *@This(), allocatedSize: usize) void {
|
||||
self.totalAllocSize -= allocatedSize;
|
||||
self.lock.lock();
|
||||
self.untrackedAllocationsCount -= 1;
|
||||
self.untrackedAllocationsSize -= allocatedSize;
|
||||
self.lock.unlock();
|
||||
}
|
||||
|
||||
pub fn getTotalMemoryUsed(self: *@This()) usize {
|
||||
return self.totalAllocSize + self.untrackedAllocationsSize + self.getTrackerUsage();
|
||||
}
|
||||
|
||||
pub fn getTrackerUsage(self: *@This()) usize {
|
||||
if (self.timeline) |timeline| {
|
||||
return timeline.events.capacity() + self.stackCompactor.?.stackArena.usage();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
var gMemTracker: ?*@This() = null;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ pub usingnamespace @import("file_dialogue.zig");
|
|||
|
||||
pub const panickers = @import("panickers.zig");
|
||||
pub const scene = @import("scene.zig");
|
||||
pub const ScenePosRot = scene.ScenePosRot;
|
||||
pub const SceneSystem = scene.SceneSystem;
|
||||
|
||||
pub const Engine = engine.Engine;
|
||||
|
|
@ -116,6 +117,21 @@ pub const script = @import("script.zig");
|
|||
pub const stacks = @import("stacks.zig");
|
||||
pub const walkAndPrintStack = stacks.walkAndPrintStack;
|
||||
|
||||
pub const gameObject = @import("gameObject.zig");
|
||||
|
||||
pub const SpawnParameters = gameObject.SpawnParameters;
|
||||
pub const GameObject = gameObject.GameObject;
|
||||
pub const GameObjectSystem = gameObject.GameObjectSystem;
|
||||
pub const MessageInfo = gameObject.MessageInfo;
|
||||
|
||||
pub fn registerObject(comptime T: type, name: []const u8) !void {
|
||||
try get(GameObjectSystem).registerObject(name, T);
|
||||
}
|
||||
|
||||
pub fn registerObjectAdvanced(comptime T: type, name: []const u8, comptime funcName: []const u8) !void {
|
||||
try get(GameObjectSystem).registerObjectAdvanced(name, T, funcName);
|
||||
}
|
||||
|
||||
pub fn fs() *PackerFS {
|
||||
return gPackerFS;
|
||||
}
|
||||
|
|
@ -143,6 +159,7 @@ pub fn getSessionStamp() i64 {
|
|||
// a struct can be used like a list of types in this way
|
||||
pub const ComponentList = struct {
|
||||
pub const Scene = scene.Scene;
|
||||
pub const GameObject = gameObject.GameObject;
|
||||
};
|
||||
|
||||
pub const ModuleStartupError = error{StartupFailed};
|
||||
|
|
@ -209,6 +226,8 @@ pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.All
|
|||
try algorithm.string_pool.setup(allocator);
|
||||
|
||||
_ = try gEngine.createObject(script_bindings.ScriptTicks, .{ .can_tick = true });
|
||||
|
||||
_ = try createObject(GameObjectSystem, .{ .can_tick = true });
|
||||
_ = try inputs.initInputStack();
|
||||
|
||||
// components define
|
||||
|
|
@ -378,6 +397,10 @@ pub fn getEngineObject(comptime T: type) ?*T {
|
|||
return null;
|
||||
}
|
||||
|
||||
pub fn get(comptime T: type) *T {
|
||||
return EngineObject(T).get();
|
||||
}
|
||||
|
||||
pub fn loadModule(moduleName: []const u8, watch: bool) !void {
|
||||
if (watch == false) unreachable; // not implemented
|
||||
|
||||
|
|
@ -426,3 +449,12 @@ pub fn SlackStruct(comptime T: type, comptime SlackSize: usize) type {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn cast(comptime T: type, p: *anyopaque) T {
|
||||
return @alignCast(@ptrCast(p));
|
||||
}
|
||||
|
||||
pub fn MakeNameFmt(comptime fmt: []const u8, args: anytype) Name {
|
||||
var makeNameBuf: [256]u8 = undefined;
|
||||
return algorithm.MakeName(std.fmt.bufPrint(&makeNameBuf, fmt, args) catch unreachable);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ pub fn destroyEntity(e: Entity) void {
|
|||
entityEntry.containers.deinit(registry.allocator);
|
||||
registry.baseSet.destroyObject(e.handle);
|
||||
} else {
|
||||
core.engine_log("UNABLE TO DESTROY ENTITY {d}", .{e.handle.index});
|
||||
// core.engine_log("UNABLE TO DESTROY ENTITY {d}", .{e.handle.index});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ pub fn CreateEntity_Lua(state: lua.LuaState) i32 {
|
|||
|
||||
pub fn setup(allocator: std.mem.Allocator) !void {
|
||||
try ComponentRef.setupFormatBuffer(allocator);
|
||||
_ = try core.createObject(EcsRegistry, .{ .can_tick = true });
|
||||
_ = try core.createObject(EcsRegistry, .{ .can_tick = true, .isCore = true });
|
||||
}
|
||||
|
||||
pub fn shutdown() void {
|
||||
|
|
@ -349,8 +349,10 @@ pub const Entity = struct {
|
|||
},
|
||||
};
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
destroyEntity(self.*);
|
||||
pub fn destroy(self: @This()) void {
|
||||
//if (core.getEngineObject(EcsRegistry) != null) {
|
||||
destroyEntity(self);
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn fromHandle(handle: core.ObjectHandle) @This() {
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ pub const Engine = struct {
|
|||
core.engine_err("RECURSIVE OBJECT CREATION NOT ALLOWED", .{});
|
||||
return error.BadInit;
|
||||
}
|
||||
|
||||
self.engineObjectLUF = self.frameNumber; // bump this every time an engine object mutation has happened
|
||||
self.createObjectLock = true;
|
||||
defer self.createObjectLock = false;
|
||||
|
|
@ -173,6 +174,9 @@ pub const Engine = struct {
|
|||
try self.engineObjects.append(self.allocator, newObjectRef);
|
||||
|
||||
if (vtable.singletonName) |singletonName| {
|
||||
if (self.engineObjectsByName.contains(vtable.singletonName.?)) {
|
||||
return error.DuplicateEngineObject;
|
||||
}
|
||||
try self.engineObjectsByName.put(self.allocator, singletonName, newObjectRef);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,192 @@
|
|||
// ok
|
||||
//
|
||||
// lets really cook a good API here
|
||||
|
||||
pub const SpawnObjectResult = struct { ptr: *anyopaque, entity: core.Entity };
|
||||
|
||||
pub const ObjectCreateFn = *const fn (allocator: std.mem.Allocator, entity: core.Entity, parameters: SpawnParameters) ObjectError!*anyopaque;
|
||||
pub const MessageHandlerFunc = *const fn (*anyopaque, *const MessageInfo, *anyopaque) void;
|
||||
|
||||
pub const GameObjectInterfaceVTable = struct {
|
||||
destroy: *const fn (*anyopaque, std.mem.Allocator) void,
|
||||
tick: ?*const fn (*anyopaque, f64) void,
|
||||
objectTypeName: core.Name = core.DefineName("UnknownType"),
|
||||
objectBaseName: core.Name = core.DefineName("UnknownObject"),
|
||||
create: ObjectCreateFn,
|
||||
messageHandlers: std.AutoHashMapUnmanaged(u32, MessageHandlerFunc) = .{},
|
||||
|
||||
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
|
||||
self.messageHandlers.deinit(allocator);
|
||||
}
|
||||
};
|
||||
|
||||
pub const ObjectError = error{
|
||||
OutOfMemory,
|
||||
BadInit,
|
||||
UnknownState,
|
||||
UnknownObject,
|
||||
};
|
||||
|
||||
pub const MessageInfo = struct {
|
||||
tag: core.Name,
|
||||
sourceEntity: ?core.Entity = null,
|
||||
sourceSystem: ?core.Name = null,
|
||||
// maybe add like a filter type? or an invoker?
|
||||
};
|
||||
|
||||
pub const GameObjectRef = struct {
|
||||
table: *GameObjectInterfaceVTable,
|
||||
ptr: *anyopaque,
|
||||
};
|
||||
|
||||
pub const GameObject = struct {
|
||||
pub var BaseContainer: *core.SparseSet(GameObject) = undefined;
|
||||
pub const ComponentName = "GameObjectComponent";
|
||||
pub const ScriptExports: []const []const u8 = &.{};
|
||||
|
||||
// set by GameObjectSystem when it calls Entity
|
||||
objectRef: GameObjectRef = undefined,
|
||||
destroyed: bool = false,
|
||||
|
||||
pub fn deinitECS(self: *@This(), handle: core.ObjectHandle) void {
|
||||
_ = handle;
|
||||
if (!self.destroyed) {
|
||||
self.destroyed = true;
|
||||
self.objectRef.table.destroy(self.objectRef.ptr, core.get(GameObjectSystem).allocator);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const SpawnParameters = struct {
|
||||
posRot: core.ScenePosRot = .{},
|
||||
scale: core.Vectorf = .{ .z = 1.0, .y = 1.0, .x = 1.0 },
|
||||
};
|
||||
|
||||
pub const SpawnEvent = struct {
|
||||
interface: GameObjectInterfaceVTable,
|
||||
};
|
||||
|
||||
pub const GameObjectSystem = struct {
|
||||
|
||||
// this is the new one, GameObjectList should be deleted after this passes initial usability
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "core.GameObjectSystem");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
objectDefinitions: std.AutoHashMapUnmanaged(u32, GameObjectInterfaceVTable) = .{},
|
||||
typesArena: std.heap.ArenaAllocator,
|
||||
|
||||
objectSpawnEvents: std.ArrayListUnmanaged(SpawnEvent) = .{},
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.typesArena = std.heap.ArenaAllocator.init(self.allocator),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn spawnObject(
|
||||
self: *@This(),
|
||||
comptime T: type,
|
||||
objectName: []const u8,
|
||||
parameters: SpawnParameters,
|
||||
) !*T {
|
||||
var n = core.MakeName(objectName);
|
||||
return self.spawnObjectByName(T, &n, parameters);
|
||||
}
|
||||
|
||||
pub fn spawnObjectByName(self: *@This(), comptime T: type, objectName: *core.Name, parameters: SpawnParameters) !*T {
|
||||
return @ptrCast(@alignCast((try self.spawnObjectFromTableByName(objectName, parameters)).ptr));
|
||||
}
|
||||
|
||||
pub fn spawnObjectFromTableByName(self: *@This(), objectName: *core.Name, parameters: SpawnParameters) !SpawnObjectResult {
|
||||
const interface: *GameObjectInterfaceVTable = self.objectDefinitions.getPtr(objectName.handle()) orelse {
|
||||
return ObjectError.UnknownObject;
|
||||
};
|
||||
|
||||
const entity = try core.createEntity();
|
||||
const objectComponent = entity.addComponent(GameObject).?;
|
||||
const rv = SpawnObjectResult{ .ptr = try interface.create(self.allocator, entity, parameters), .entity = entity };
|
||||
|
||||
objectComponent.objectRef = .{
|
||||
.table = interface,
|
||||
.ptr = rv.ptr,
|
||||
};
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// registers an object for spawning with the object name
|
||||
pub fn registerObject(self: *@This(), objectName: []const u8, comptime T: type) !void {
|
||||
try self.registerObjectAdvanced(objectName, T, "create");
|
||||
}
|
||||
|
||||
pub fn registerObjectAdvanced(self: *@This(), name: []const u8, comptime T: type, comptime createFunctionName: []const u8) !void {
|
||||
var n = core.MakeName(name);
|
||||
|
||||
const Wrap = struct {
|
||||
pub fn create(allocator: std.mem.Allocator, entity: core.Entity, parameters: SpawnParameters) ObjectError!*anyopaque {
|
||||
return @field(T, createFunctionName)(allocator, entity, parameters) catch {
|
||||
return ObjectError.BadInit;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn destroy(p: *anyopaque, alloc: std.mem.Allocator) void {
|
||||
T.destroy(@ptrCast(@alignCast(p)), alloc);
|
||||
}
|
||||
|
||||
pub fn tick(p: *anyopaque, dt: f64) void {
|
||||
T.tick(@ptrCast(@alignCast(p)), dt);
|
||||
}
|
||||
};
|
||||
|
||||
var interface = GameObjectInterfaceVTable{
|
||||
.create = Wrap.create,
|
||||
.tick = if (@hasDecl(T, "tick")) Wrap.tick else null,
|
||||
.destroy = Wrap.destroy,
|
||||
.objectTypeName = core.MakeTypeName(T),
|
||||
.objectBaseName = n,
|
||||
.messageHandlers = .{},
|
||||
};
|
||||
|
||||
if (@hasDecl(T, "MessageHandlers")) {
|
||||
inline for (T.MessageHandlers) |handlerName| {
|
||||
var messageHandlerName = core.MakeName(handlerName);
|
||||
// const handlerFunc = @field(T, handlerName);
|
||||
// const handlerTypeInfo = @typeInfo(@TypeOf(handlerFunc));
|
||||
// const M = @typeInfo(handlerTypeInfo.@"fn".params[3].type.?).pointer.child;
|
||||
|
||||
const HandlerWrap = struct {
|
||||
pub fn messageHandler(p: *anyopaque, info: *const MessageInfo, message: *anyopaque) void {
|
||||
@field(T, handlerName)(@ptrCast(@alignCast(p)), info, @ptrCast(@alignCast(message)));
|
||||
}
|
||||
};
|
||||
|
||||
try interface.messageHandlers.put(self.typesArena.allocator(), messageHandlerName.handle(), HandlerWrap.messageHandler);
|
||||
}
|
||||
}
|
||||
|
||||
try self.objectDefinitions.put(self.typesArena.allocator(), n.handle(), interface);
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), dt: f64) void {
|
||||
// for (self.objects.items) |object| {
|
||||
// if (object.vtable.tick) |tick_fn| {
|
||||
// tick_fn(object.ptr, dt);
|
||||
// }
|
||||
// }
|
||||
_ = self;
|
||||
_ = dt;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.typesArena.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
const core = @import("core.zig");
|
||||
const std = @import("std");
|
||||
|
|
|
|||
|
|
@ -565,6 +565,13 @@ pub const InputStack = struct {
|
|||
|
||||
keysDown: std.AutoHashMapUnmanaged(Key, bool) = .{},
|
||||
|
||||
mousePosition: core.Vector2f = .{},
|
||||
|
||||
pub fn setMousePosition(self: *@This(), x: f32, y: f32) void {
|
||||
self.mousePosition.x = x;
|
||||
self.mousePosition.y = y;
|
||||
}
|
||||
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.InputStack");
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
|
|
|
|||
|
|
@ -159,6 +159,18 @@ pub fn Vector2Type(comptime T: type, comptime typeName: []const u8) type {
|
|||
};
|
||||
}
|
||||
|
||||
pub inline fn negate(self: @This()) @This() {
|
||||
return .{ .x = -self.x, .y = self.y };
|
||||
}
|
||||
|
||||
pub inline fn cross(self: @This(), other: @This()) T {
|
||||
return self.x * other.y - self.y * other.x;
|
||||
}
|
||||
|
||||
pub inline fn removeComponent(self: @This(), other: @This()) @This() {
|
||||
return self.sub(other.fmul(self.dot(other)));
|
||||
}
|
||||
|
||||
pub inline fn from(o: anytype) @This() {
|
||||
const OType: std.builtin.Type = @typeInfo(@TypeOf(o.x));
|
||||
switch (@typeInfo(T)) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ pub const SceneMobilityMode = enum {
|
|||
moveable, // sceneobject is moveable and has it's final transform updated
|
||||
};
|
||||
|
||||
pub const SceneObjectPosRot = struct {
|
||||
pub const ScenePosRot = struct {
|
||||
position: core.Vectorf = .{ .x = 0, .y = 0, .z = 0 },
|
||||
rotation: core.Rotation = core.Rotation.init(),
|
||||
scale: core.Vectorf = core.Vectorf.new(1.0, 1.0, 1.0),
|
||||
|
|
@ -60,7 +60,7 @@ pub const SceneObjectRepr = struct {
|
|||
|
||||
pub const SceneObject = struct {
|
||||
_repr: SceneObjectRepr = .{}, // not public
|
||||
posRot: SceneObjectPosRot = .{}, // position and rotation
|
||||
posRot: ScenePosRot = .{}, // position and rotation
|
||||
settings: SceneObjectSettings = .{}, //
|
||||
children: ArrayListUnmanaged(core.ObjectHandle) = .{},
|
||||
|
||||
|
|
@ -137,6 +137,14 @@ pub const Scene = struct {
|
|||
core.engine_log("handle.index = 0x{x} generation = {d} alive={any}", .{ self.handle.index, self.handle.generation, self.handle.alive });
|
||||
}
|
||||
|
||||
pub fn setPosRot(self: @This(), newPosRot: ScenePosRot) void {
|
||||
if (SceneObjectContainer.get(self.handle, .posRot)) |posRot| {
|
||||
posRot.* = newPosRot;
|
||||
} else {
|
||||
core.engine_log("setposRot failed handle.index = 0x{x} generation = {d} alive={any}", .{ self.handle.index, self.handle.generation, self.handle.alive });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setPosition(self: @This(), position: core.Vectorf) void {
|
||||
if (SceneObjectContainer.get(self.handle, .posRot)) |posRot| {
|
||||
posRot.*.position = position;
|
||||
|
|
@ -145,7 +153,7 @@ pub const Scene = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn getPosRot(self: *@This()) *SceneObjectPosRot {
|
||||
pub fn getPosRot(self: *@This()) *ScenePosRot {
|
||||
return SceneObjectContainer.get(self.handle, .posRot).?;
|
||||
}
|
||||
|
||||
|
|
@ -214,6 +222,8 @@ pub const Scene = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn getTickCount() u32 {}
|
||||
|
||||
pub fn getAndResolveTransform(self: @This()) core.Transform {
|
||||
const repr: *SceneObjectRepr = SceneObjectContainer.get(self.handle, ._repr).?;
|
||||
if (repr.lastUpdate != getSceneSystem().tickCount) {
|
||||
|
|
@ -227,6 +237,12 @@ pub const Scene = struct {
|
|||
return SceneObjectContainer.get(self.handle, ._repr).?.transform;
|
||||
}
|
||||
|
||||
pub fn updateTransform(self: @This()) void {
|
||||
const repr = SceneObjectContainer.get(self.handle, ._repr).?;
|
||||
const posRot = SceneObjectContainer.get(self.handle, .posRot).?;
|
||||
getSceneSystem().updateTransform(repr, posRot);
|
||||
}
|
||||
|
||||
// you MUST clearTransfomRefUnsafe() before destroying this transform
|
||||
pub fn setTransformRefUnsafe(self: @This(), ref: *core.Transform) void {
|
||||
SceneObjectContainer.get(self.handle, ._repr).?.transformOverride = ref;
|
||||
|
|
@ -288,7 +304,7 @@ pub const SceneSystem = struct {
|
|||
pub const FieldType = SceneObjectSet.FieldType;
|
||||
|
||||
// internal update transform function
|
||||
fn updateTransform(self: *@This(), repr: *SceneObjectRepr, posRot: *const SceneObjectPosRot) void {
|
||||
fn updateTransform(self: *@This(), repr: *SceneObjectRepr, posRot: *const ScenePosRot) void {
|
||||
if (repr.lastUpdate == self.tickCount) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -385,6 +401,7 @@ pub const SceneSystem = struct {
|
|||
pub fn tick(self: *@This(), deltaTime: f64) void {
|
||||
var z = tracy.ZoneNC(@src(), "Scene System Tick", 0xAABBDD);
|
||||
defer z.End();
|
||||
|
||||
self.updateTransforms();
|
||||
_ = deltaTime;
|
||||
}
|
||||
|
|
@ -392,7 +409,6 @@ pub const SceneSystem = struct {
|
|||
pub fn deinit(self: *@This()) void {
|
||||
self.dynamicObjects.deinit(self.allocator);
|
||||
self.childrenArena.deinit();
|
||||
// core.undefineComponent(Scene);
|
||||
Scene.SceneObjectContainer.destroy();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// helper, add this to your game manager class and invoke it's tick,
|
||||
// helper, add this to your game manager class and invoke it's tick.
|
||||
//
|
||||
// will automatically manage spawning and despawning entities and manage de-initialization for you
|
||||
// i actually don't reccomend using this for the most part for more serious games
|
||||
|
|
@ -8,7 +8,9 @@
|
|||
//
|
||||
// however if you want to define prefabs, then something like this might be useful.
|
||||
//
|
||||
// Like i said, more geared towards gamejams and toys than serious work.
|
||||
// if this were to be a bit more serious...
|
||||
//
|
||||
// how would i use it.
|
||||
|
||||
pub const GameObjectInterfaceVTable = struct {
|
||||
destroy: *const fn (*anyopaque) void,
|
||||
|
|
@ -42,6 +44,7 @@ pub const GameObjectList = struct {
|
|||
allocator: std.mem.Allocator,
|
||||
|
||||
// slots might be better? allow for stable indexes?
|
||||
// index pool?
|
||||
objects: std.ArrayListUnmanaged(GameObjectInterface) = .{},
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ pub const list = [_][]const u8{
|
|||
"platform",
|
||||
"assets",
|
||||
"audio",
|
||||
"net",
|
||||
"physics",
|
||||
|
||||
"rend",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
const std = @import("std");
|
||||
|
||||
const depList = [_][]const u8{
|
||||
"core",
|
||||
"enet",
|
||||
};
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false;
|
||||
|
||||
const mod = b.addModule("net", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("src/net.zig"),
|
||||
});
|
||||
|
||||
for (depList) |depName| {
|
||||
const dep = b.dependency(depName, .{ .target = target, .optimize = optimize, .static_build = static_build });
|
||||
|
||||
mod.addImport(depName, dep.module(depName));
|
||||
}
|
||||
|
||||
const test_step = b.step("test", "run unit tests for net");
|
||||
const tests = b.addTest(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("tests/tests.zig"),
|
||||
});
|
||||
|
||||
tests.root_module.addImport("net", mod);
|
||||
const runArtifact = b.addRunArtifact(tests);
|
||||
test_step.dependOn(&runArtifact.step);
|
||||
b.installArtifact(tests);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
.{
|
||||
.name = .net,
|
||||
.version = "0.0.0",
|
||||
.dependencies = .{
|
||||
.core = .{ .path = "../core" },
|
||||
.enet = .{ .path = "../../lib/enet" },
|
||||
},
|
||||
.paths = .{
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,426 @@
|
|||
pub var TransportInterfaceVTable = net.TransportInterface.Implement(@This());
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "net.ENetTransport");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
sessions: std.ArrayListUnmanaged(*net.Session) = .{},
|
||||
deadSessions: std.ArrayListUnmanaged(*net.Session) = .{},
|
||||
|
||||
const MAX_CLIENTS = 128;
|
||||
const DEFAULT_PORT = 7777;
|
||||
const ConnectionTimeout = 10000;
|
||||
|
||||
const UnreliableChannel = 0;
|
||||
const ReliableChannel = 1;
|
||||
const ChannelCount = 1;
|
||||
|
||||
// to implement the new links and sessions interface.
|
||||
//
|
||||
const QueuedLinkData = struct {
|
||||
bytes: []u8,
|
||||
reliable: bool,
|
||||
};
|
||||
|
||||
const ENetLinkData = struct {
|
||||
peer: [*c]enet.ENetPeer,
|
||||
link: *net.Link,
|
||||
|
||||
// todo- split into reliable and unreliable
|
||||
// also maybe move to the upper level net.Link instead of LinkData
|
||||
queuedMessages: core.RingQueueU(QueuedLinkData),
|
||||
|
||||
testLinkPosition: core.Vectorf = .{},
|
||||
|
||||
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
|
||||
self.queuedMessages.deinit();
|
||||
allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn queuePacketData(self: *@This(), bytes: []const u8, reliable: bool) void {
|
||||
const allocator = net.netAllocator();
|
||||
self.queuedMessages.push(.{
|
||||
.bytes = allocator.dupe(u8, bytes) catch unreachable,
|
||||
.reliable = reliable,
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn readDebugVector(self: *@This(), data: []const u8) void {
|
||||
self.testLinkPosition = @as(*const core.Vectorf, @ptrCast(@alignCast(data.ptr))).*;
|
||||
//core.engine_log("reading vector position: {d} {d} {d}", self.testLinkPosition);
|
||||
}
|
||||
|
||||
pub fn pushDebugLoc(self: *@This()) void {
|
||||
// const x = self.testLinkPosition;
|
||||
// core.engine_log("sending vector position: {d} {d} {d}", x);
|
||||
|
||||
const allocator = net.netAllocator();
|
||||
self.queuedMessages.push(.{
|
||||
.bytes = allocator.dupe(u8, &@as([@sizeOf(core.Vectorf)]u8, @bitCast(self.testLinkPosition))) catch unreachable,
|
||||
.reliable = true,
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
// raw access to sendPacket
|
||||
pub fn sendPacket(self: *@This(), bytes: []const u8, reliable: bool) void {
|
||||
if (enet.enet_packet_create(bytes.ptr, bytes.len, if (reliable) enet.ENET_PACKET_FLAG_RELIABLE else 0)) |packet| {
|
||||
_ = enet.enet_peer_send(self.peer, if (reliable) 0 else 1, packet);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const ENetTransport = @This();
|
||||
|
||||
const ENetSessionData = struct {
|
||||
address: enet.ENetAddress,
|
||||
host: [*c]enet.ENetHost,
|
||||
session: *net.Session,
|
||||
messageCount: u32 = 0,
|
||||
|
||||
debugTick: f64 = 0.05,
|
||||
|
||||
pub fn findLinkByPeer(self: *@This(), peer: [*c]enet.ENetPeer) ?*net.Link {
|
||||
for (self.session.links.items) |link| {
|
||||
if (getLinkData(link).peer == peer) {
|
||||
return link;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), dt: f64) void {
|
||||
var event: enet.ENetEvent = undefined;
|
||||
|
||||
// Service the host with a 1000ms timeout
|
||||
const result = enet.enet_host_service(self.host, &event, 0);
|
||||
|
||||
if (result > 0) {
|
||||
switch (event.type) {
|
||||
enet.ENET_EVENT_TYPE_CONNECT => {
|
||||
// core.engine_log("a client connected, creating link", .{});
|
||||
const welcome_msg = "msg: Welcome to ENet test server!";
|
||||
const link = core.get(ENetTransport).createLink(self.session) catch unreachable;
|
||||
const linkData = getLinkData(link);
|
||||
linkData.peer = event.peer;
|
||||
linkData.sendPacket(welcome_msg, true);
|
||||
link.linkType = .server;
|
||||
},
|
||||
enet.ENET_EVENT_TYPE_RECEIVE => {
|
||||
self.messageCount += 1;
|
||||
const data = @as([*]u8, @ptrCast(event.packet.*.data))[0..event.packet.*.dataLength];
|
||||
// core.engine_log("Received message #{}: '{s}' echoing it back", .{ self.messageCount, data });
|
||||
|
||||
if (self.findLinkByPeer(event.peer)) |link| {
|
||||
const linkData = getLinkData(link);
|
||||
if (link.linkType == .server) {
|
||||
linkData.queuePacketData(data, true);
|
||||
}
|
||||
if (link.linkType == .client) {
|
||||
if (!std.mem.startsWith(u8, data, "msg:")) {
|
||||
linkData.readDebugVector(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy the received packet
|
||||
enet.enet_packet_destroy(event.packet);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
for (self.session.links.items) |link| {
|
||||
const linkData = getLinkData(link);
|
||||
|
||||
self.debugTick -= dt;
|
||||
if (self.debugTick < 0) {
|
||||
self.debugTick = 0.05;
|
||||
if (link.linkType == .server) {
|
||||
linkData.pushDebugLoc();
|
||||
}
|
||||
}
|
||||
|
||||
while (linkData.queuedMessages.pop()) |queuedData| {
|
||||
linkData.sendPacket(queuedData.bytes, queuedData.reliable);
|
||||
net.netAllocator().free(queuedData.bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
try enet_mod.initialize();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn createSession(self: *@This(), address: ?enet.ENetAddress) !*net.Session {
|
||||
const session = try net.netAllocator().create(net.Session);
|
||||
session.* = .{
|
||||
.transportName = "ENet",
|
||||
.transport = .{ .vtable = TransportInterfaceVTable, .ptr = self },
|
||||
.allocator = net.netAllocator(),
|
||||
};
|
||||
|
||||
const transportData = try net.netAllocator().create(ENetSessionData);
|
||||
transportData.session = session;
|
||||
|
||||
if (address != null) {
|
||||
transportData.address = address.?;
|
||||
transportData.host = enet.enet_host_create(&transportData.address, MAX_CLIENTS, 2, 0, 0) orelse {
|
||||
return error.ServerStartFailed;
|
||||
};
|
||||
net.log("host created on port {d}", .{address.?.port});
|
||||
} else {
|
||||
transportData.host = enet.enet_host_create(null, 1, 2, 0, 0) orelse {
|
||||
return error.ClientStartFailed;
|
||||
};
|
||||
transportData.address = .{
|
||||
.host = 0,
|
||||
.port = 0,
|
||||
};
|
||||
net.log("client session created", .{});
|
||||
}
|
||||
|
||||
session.transportData = transportData;
|
||||
try self.sessions.append(self.allocator, session);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
pub fn hostSession(self: *@This(), bindInfo: ?[]const []const u8) !*net.Session {
|
||||
const port: u16 = try std.fmt.parseInt(u16, bindInfo.?[0], 0);
|
||||
const session = try self.createSession(enet.ENetAddress{
|
||||
.host = enet.ENET_HOST_ANY,
|
||||
.port = port,
|
||||
});
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
pub fn endSession(self: *@This(), session: *net.Session) void {
|
||||
if (session.transportData) |transportData| {
|
||||
const td = core.cast(*ENetSessionData, transportData);
|
||||
enet.enet_host_destroy(td.host);
|
||||
}
|
||||
|
||||
for (self.sessions.items, 0..) |s, i| {
|
||||
if (s == session) {
|
||||
_ = self.sessions.orderedRemove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn printIp(ip: u32) void {
|
||||
core.engine_log("{d}.{d}.{d}.{d}", .{
|
||||
(ip >> 0) & 0xFF,
|
||||
(ip >> 8) & 0xFF,
|
||||
(ip >> 16) & 0xFF,
|
||||
(ip >> 24) & 0xFF,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn parseConnectTarget(allocator: std.mem.Allocator, in: []const u8) !struct {
|
||||
port: u16,
|
||||
address: [:0]u8,
|
||||
} {
|
||||
var portStr: ?[]const u8 = null;
|
||||
var base: []const u8 = in;
|
||||
|
||||
var j: usize = in.len;
|
||||
while (j > 0) : (j -= 1) {
|
||||
const i = j - 1;
|
||||
|
||||
if (in[i] == ':') {
|
||||
portStr = in[j..in.len];
|
||||
base = in[0..i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var p: u16 = DEFAULT_PORT;
|
||||
|
||||
if (portStr) |ps| {
|
||||
p = try std.fmt.parseInt(u16, ps, 10);
|
||||
}
|
||||
|
||||
return .{ .port = p, .address = try std.fmt.allocPrintZ(allocator, "{s}", .{base}) };
|
||||
}
|
||||
|
||||
pub fn createLink(self: *@This(), session: *net.Session) !*net.Link {
|
||||
const newLink = try net.netAllocator().create(net.Link);
|
||||
|
||||
newLink.* = .{
|
||||
.transport = .{ .vtable = TransportInterfaceVTable, .ptr = self },
|
||||
.allocator = net.netAllocator(),
|
||||
};
|
||||
// errdefer newLink.destroy();
|
||||
|
||||
const linkInfo = try net.netAllocator().create(ENetLinkData);
|
||||
errdefer self.allocator.destroy(linkInfo);
|
||||
|
||||
linkInfo.* = .{
|
||||
.link = newLink,
|
||||
.peer = null,
|
||||
.queuedMessages = try core.RingQueueU(QueuedLinkData).init(net.netAllocator(), 4092),
|
||||
};
|
||||
|
||||
newLink.transportData = linkInfo;
|
||||
newLink.session = session;
|
||||
|
||||
try session.addLink(newLink);
|
||||
|
||||
return newLink;
|
||||
}
|
||||
|
||||
pub fn getSessionData(session: *net.Session) *ENetSessionData {
|
||||
return core.cast(*ENetSessionData, session.transportData.?);
|
||||
}
|
||||
|
||||
pub fn getLinkData(link: *net.Link) *ENetLinkData {
|
||||
return core.cast(*ENetLinkData, link.transportData.?);
|
||||
}
|
||||
|
||||
// creates a session and a link with that session, to the host
|
||||
pub fn connect(self: *@This(), target: []const u8) !*net.Link {
|
||||
const rv = try parseConnectTarget(self.allocator, target);
|
||||
defer self.allocator.free(rv.address);
|
||||
|
||||
// parse address to target
|
||||
const session = try self.createSession(null);
|
||||
errdefer {
|
||||
session.skipEndSession = true;
|
||||
session.destroy();
|
||||
}
|
||||
|
||||
const newLink = try self.createLink(session);
|
||||
newLink.linkType = .client;
|
||||
const linkData = core.cast(*ENetLinkData, newLink.transportData.?);
|
||||
|
||||
// Set up server address
|
||||
const sessionInfo = getSessionData(session);
|
||||
|
||||
// Resolve server hostname
|
||||
if (enet.enet_address_set_host(&sessionInfo.address, rv.address.ptr) != 0) {
|
||||
std.debug.print("Failed to resolve server address: {s}\n", .{rv.address});
|
||||
return error.AddressResolutionFailed;
|
||||
}
|
||||
|
||||
sessionInfo.address.port = rv.port;
|
||||
|
||||
// Connect to server
|
||||
linkData.peer = enet.enet_host_connect(sessionInfo.host, &sessionInfo.address, ChannelCount, 0);
|
||||
if (linkData.peer == null) {
|
||||
std.debug.print("Failed to create connection to server\n", .{});
|
||||
return error.ConnectionFailed;
|
||||
}
|
||||
|
||||
std.debug.print("connecting to server [{s}] [{d}]\n", .{ rv.address, rv.port });
|
||||
var event: enet.ENetEvent = undefined;
|
||||
if (enet.enet_host_service(sessionInfo.host.?, &event, ConnectionTimeout) > 0 and event.type == enet.ENET_EVENT_TYPE_CONNECT) {
|
||||
std.debug.print("Connected to server successfully!\n", .{});
|
||||
newLink.state = .connecting;
|
||||
newLink.session = session;
|
||||
} else {
|
||||
std.debug.print("Failed to connect to server within timeout event.type {d} \n", .{event.type});
|
||||
enet.enet_peer_reset(linkData.peer);
|
||||
return error.ConnectionTimeout;
|
||||
}
|
||||
|
||||
return newLink;
|
||||
}
|
||||
|
||||
pub fn endLink(self: *@This(), link: *net.Link) void {
|
||||
const linkData = getLinkData(link);
|
||||
|
||||
enet.enet_peer_reset(linkData.peer);
|
||||
|
||||
net.netAllocator().destroy(linkData);
|
||||
self.deadSessions.append(self.allocator, link.session.?) catch {};
|
||||
}
|
||||
|
||||
pub fn sendMessageLink(self: *@This(), link: *net.Link, data: []const u8, reliable: bool) !void {
|
||||
_ = self;
|
||||
|
||||
const linkData = getLinkData(link);
|
||||
linkData.queuePacketData(data, reliable);
|
||||
}
|
||||
|
||||
pub fn preTick(self: *@This(), dt: f64) !void {
|
||||
//switch (self.peerType) {
|
||||
// .server => {
|
||||
// self.tickServer(dt);
|
||||
// },
|
||||
// .client => {
|
||||
// self.tickClient(dt);
|
||||
// },
|
||||
//else => {},
|
||||
// }
|
||||
|
||||
for (self.sessions.items) |session| {
|
||||
const sessionInfo = getSessionData(session);
|
||||
sessionInfo.tick(dt);
|
||||
|
||||
// tick messages in here
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tickServer(self: *@This(), dt: f64) void {
|
||||
_ = dt;
|
||||
|
||||
var event: enet.ENetEvent = undefined;
|
||||
|
||||
const result = enet.enet_host_service(self.host.?, &event, 0);
|
||||
|
||||
if (result > 0) {
|
||||
switch (event.type) {
|
||||
enet.ENET_EVENT_TYPE_CONNECT => {
|
||||
core.engine_log("client connected!!!!");
|
||||
},
|
||||
enet.ENET_EVENT_TYPE_RECEIVE => {
|
||||
const data = @as([*]u8, @ptrCast(event.packet.*.data))[0..event.packet.*.dataLength];
|
||||
_ = data;
|
||||
enet.enet_packet_destroy(event.packet);
|
||||
},
|
||||
|
||||
enet.ENET_EVENT_TYPE_DISCONNECT => {
|
||||
for (self.clients.items, 0..) |client, i| {
|
||||
if (client.id == event.peer.*.connectID) {
|
||||
_ = self.clients.swapRemove(i);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
else => {},
|
||||
}
|
||||
} else if (result < 0) {
|
||||
net.err("Error servicing host\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tickClient(self: *@This(), dt: f64) void {
|
||||
_ = self;
|
||||
_ = dt;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
for (self.sessions.items) |session| {
|
||||
session.destroy();
|
||||
}
|
||||
self.sessions.deinit(self.allocator);
|
||||
self.deadSessions.deinit(self.allocator);
|
||||
|
||||
enet_mod.deinitialize();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
const core = @import("core");
|
||||
const net = @import("../net.zig");
|
||||
const std = @import("std");
|
||||
const enet_mod = @import("enet");
|
||||
const enet = enet_mod.c;
|
||||
|
|
@ -0,0 +1,307 @@
|
|||
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.allocPrintZ(allocator, "{d}.{d}.{d}.{d}", .{
|
||||
(ip >> 0) & 0xFF,
|
||||
(ip >> 8) & 0xFF,
|
||||
(ip >> 16) & 0xFF,
|
||||
(ip >> 24) & 0xFF,
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
const core = @import("core");
|
||||
const net = @import("net.zig");
|
||||
const std = @import("std");
|
||||
pub const EnetTransport = @import("enet/EnetTransport.zig");
|
||||
|
||||
pub fn net_log(comptime fmt: []const u8, args: anytype) void {
|
||||
core.printInner("[NET ]: " ++ fmt ++ "\n", args);
|
||||
}
|
||||
|
||||
pub fn net_logs(comptime fmt: []const u8) void {
|
||||
core.printInner("[NET ]: " ++ fmt ++ "\n", .{});
|
||||
}
|
||||
|
||||
pub fn net_err(comptime fmt: []const u8, args: anytype) void {
|
||||
core.printInner("[NET ]: ERROR!! " ++ fmt ++ "\n", args);
|
||||
}
|
||||
|
||||
pub fn net_errs(comptime fmt: []const u8) void {
|
||||
core.printInner("[NET ]: ERROR!! " ++ fmt ++ "\n", .{});
|
||||
}
|
||||
|
||||
pub const TransportRef = core.Reference(net.TransportInterface);
|
||||
|
||||
pub const NetEngine = struct {
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "net.NetEngine");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
transport: ?TransportRef = null,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = @This(){
|
||||
.allocator = allocator,
|
||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn arenaAllocator(self: *@This()) std.mem.Allocator {
|
||||
return self.arena.allocator();
|
||||
}
|
||||
|
||||
pub fn initalizeTransport(self: *@This(), comptime T: type) !TransportRef {
|
||||
const transport = try core.createObject(T, .{ .can_tick = true });
|
||||
const interface = TransportRef{
|
||||
.ptr = transport,
|
||||
.vtable = T.TransportInterfaceVTable,
|
||||
};
|
||||
|
||||
self.transport = interface;
|
||||
|
||||
net.log("transport initialized {s}", .{@typeName(T)});
|
||||
return interface;
|
||||
}
|
||||
|
||||
pub fn shutdown(self: *@This()) void {
|
||||
net_logs("NetEngine shutting down");
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.arena.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), deltaTime: f64) void {
|
||||
_ = self;
|
||||
_ = deltaTime;
|
||||
// Network tick logic will go here
|
||||
}
|
||||
};
|
||||
|
||||
// responsibilities
|
||||
//
|
||||
// NetEngine
|
||||
// - all gameobjects and high level gameplay talks to this one, and queues up messages
|
||||
//
|
||||
// TransportInterface
|
||||
// - manages connection - startServer, stopServer, startClient, stopClient
|
||||
// -
|
||||
|
|
@ -87,10 +87,11 @@ pub const HandlerError = error{
|
|||
EventIgnored,
|
||||
};
|
||||
|
||||
pub const SingleFn = *const fn (NodeHandle, ?*anyopaque) HandlerError!void;
|
||||
pub const PressedFn = *const fn (NodeHandle, PressedType, ?*anyopaque) HandlerError!void;
|
||||
pub const SingleFn = *const fn (*papyrus.Context, NodeHandle, ?*anyopaque) HandlerError!void;
|
||||
pub const PressedFn = *const fn (*papyrus.Context, NodeHandle, PressedType, ?*anyopaque) HandlerError!void;
|
||||
|
||||
const Listener = struct {
|
||||
ctx: *papyrus.Context,
|
||||
node: NodeHandle,
|
||||
event: Type,
|
||||
context: ?*anyopaque,
|
||||
|
|
@ -99,6 +100,7 @@ const Listener = struct {
|
|||
};
|
||||
|
||||
const PressedListener = struct {
|
||||
ctx: *papyrus.Context,
|
||||
node: NodeHandle,
|
||||
keycode: Key,
|
||||
event: PressedType,
|
||||
|
|
@ -109,6 +111,7 @@ const PressedListener = struct {
|
|||
|
||||
pub fn installMouseOverEventAdvanced(
|
||||
self: *@This(),
|
||||
ctx: *papyrus.Context,
|
||||
node: NodeHandle,
|
||||
event: Type,
|
||||
context: ?*anyopaque,
|
||||
|
|
@ -118,6 +121,7 @@ pub fn installMouseOverEventAdvanced(
|
|||
const allocator = self.arena.allocator();
|
||||
|
||||
const listener: Listener = .{
|
||||
.ctx = ctx,
|
||||
.node = node,
|
||||
.event = event,
|
||||
.context = context,
|
||||
|
|
@ -136,19 +140,20 @@ pub fn installMouseOverEventAdvanced(
|
|||
|
||||
pub fn installMouseOverEvent(
|
||||
self: *@This(),
|
||||
ctx: *papyrus.Context,
|
||||
node: NodeHandle,
|
||||
event: Type,
|
||||
context: ?*anyopaque,
|
||||
eventFn: SingleFn,
|
||||
) !void {
|
||||
try installMouseOverEventAdvanced(self, node, event, context, eventFn, false);
|
||||
try installMouseOverEventAdvanced(self, ctx, node, event, context, eventFn, false);
|
||||
}
|
||||
|
||||
pub fn pushMouseOverEvent(self: *@This(), node: NodeHandle, event: Type) HandlerError!void {
|
||||
if (self.inputEvents.get(node)) |listeners| {
|
||||
for (listeners.items) |listener| {
|
||||
if (listener.event == event) {
|
||||
try listener.eventFn(node, listener.context);
|
||||
try listener.eventFn(listener.ctx, node, listener.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -164,8 +169,18 @@ pub fn pushPressedEvent(self: *@This(), node: NodeHandle, event: PressedType, ke
|
|||
}
|
||||
}
|
||||
|
||||
pub fn installOnPressedEventAdvanced(self: *@This(), node: NodeHandle, event: PressedType, keycode: Key, context: ?*anyopaque, eventFn: PressedFn, innate: bool) !void {
|
||||
pub fn installOnPressedEventAdvanced(
|
||||
self: *@This(),
|
||||
ctx: *papyrus.Context,
|
||||
node: NodeHandle,
|
||||
event: PressedType,
|
||||
keycode: Key,
|
||||
context: ?*anyopaque,
|
||||
eventFn: PressedFn,
|
||||
innate: bool,
|
||||
) !void {
|
||||
const listener: PressedListener = .{
|
||||
.ctx = ctx,
|
||||
.node = node,
|
||||
.event = event,
|
||||
.keycode = keycode,
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ pub const Context = struct {
|
|||
debugText: std.ArrayList([]u8),
|
||||
debugTextCount: u32 = 0,
|
||||
drawDebug: bool = false,
|
||||
debugMousePick: bool = false,
|
||||
rootNodes: std.ArrayList(NodeHandle),
|
||||
|
||||
const debugTextMax = 32;
|
||||
|
|
@ -438,19 +439,21 @@ pub const Context = struct {
|
|||
pub fn tickDebug(self: *@This(), deltaTime: f64) !void {
|
||||
_ = deltaTime;
|
||||
try self.pushDebugText("mouse Position: {d}, {d}", .{ self.currentCursorPosition.x, self.currentCursorPosition.y });
|
||||
if (self.mousePick.selected_node) |node| {
|
||||
const n = self.getRead(node);
|
||||
const layout = self._displayLayout.items[node.index];
|
||||
try self.pushDebugText("found node: {d},{d} size={d}x{d} layoutpos={d},{d} layoutsize={d},{d}", .{
|
||||
node.index,
|
||||
node.generation,
|
||||
n.size.x,
|
||||
n.size.y,
|
||||
layout.pos.x,
|
||||
layout.pos.y,
|
||||
layout.size.x,
|
||||
layout.size.y,
|
||||
});
|
||||
if (self.debugMousePick) {
|
||||
if (self.mousePick.selected_node) |node| {
|
||||
const n = self.getRead(node);
|
||||
const layout = self._displayLayout.items[node.index];
|
||||
try self.pushDebugText("found node: {d},{d} size={d}x{d} layoutpos={d},{d} layoutsize={d},{d}", .{
|
||||
node.index,
|
||||
node.generation,
|
||||
n.size.x,
|
||||
n.size.y,
|
||||
layout.pos.x,
|
||||
layout.pos.y,
|
||||
layout.size.x,
|
||||
layout.size.y,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -733,11 +736,11 @@ pub const Context = struct {
|
|||
self.get(button).text = LocText.fromUtf8(t);
|
||||
}
|
||||
|
||||
try self.events.installMouseOverEventAdvanced(button, .mouseOver, null, NodeProperty_Button.buttonMouseOverListener, true);
|
||||
try self.events.installMouseOverEventAdvanced(button, .mouseOff, null, NodeProperty_Button.buttonMouseOffListener, true);
|
||||
try self.events.installMouseOverEventAdvanced(self, button, .mouseOver, null, NodeProperty_Button.buttonMouseOverListener, true);
|
||||
try self.events.installMouseOverEventAdvanced(self, button, .mouseOff, null, NodeProperty_Button.buttonMouseOffListener, true);
|
||||
|
||||
try self.events.installOnPressedEventAdvanced(button, .onPressed, .Mouse1, null, NodeProperty_Button.buttonOnPressedEvent, true);
|
||||
try self.events.installOnPressedEventAdvanced(button, .onReleased, .Mouse1, null, NodeProperty_Button.buttonOnPressedEvent, true);
|
||||
try self.events.installOnPressedEventAdvanced(self, button, .onPressed, .Mouse1, null, NodeProperty_Button.buttonOnPressedEvent, true);
|
||||
try self.events.installOnPressedEventAdvanced(self, button, .onReleased, .Mouse1, null, NodeProperty_Button.buttonOnPressedEvent, true);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
|
@ -1192,6 +1195,10 @@ pub const Context = struct {
|
|||
try self.addDebugInfo(drawList);
|
||||
}
|
||||
|
||||
if (self.debugMousePick) {
|
||||
try self.mousePick.addMousePickInfo(self, drawList);
|
||||
}
|
||||
|
||||
for (0..drawList.items.len) |i| {
|
||||
switch (drawList.items[i].primitive) {
|
||||
.Text => |*text| {
|
||||
|
|
@ -1206,13 +1213,11 @@ pub const Context = struct {
|
|||
const defaultHeight = 16;
|
||||
const sizePerLine: f32 = defaultHeight + 2;
|
||||
const yOffsetPerLine: f32 = defaultHeight + 1;
|
||||
var yOffset: f32 = sizePerLine;
|
||||
var yOffset: f32 = sizePerLine * 2;
|
||||
const width = defaultHeight / 2 * 120;
|
||||
|
||||
const fontHandle = self.fontCache.defaultMonoFont.atlas.fontHandle;
|
||||
|
||||
try self.mousePick.addMousePickInfo(&self, drawList);
|
||||
|
||||
try drawList.append(.{
|
||||
.node = .{},
|
||||
.primitive = .{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ font: papyrus.Font,
|
|||
buttonStyle: ButtonStyle = .{},
|
||||
disabled: bool = false,
|
||||
buttonState: ButtonState = .Normal,
|
||||
|
||||
// documentation:
|
||||
// Each PapyrusNode has a common set of fields, these are defined in PapyrusNode
|
||||
//
|
||||
|
|
@ -118,24 +117,21 @@ pub fn addToDrawList(dlb: DrawListBuilder) !void {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn buttonMouseOverListener(node: papyrus.NodeHandle, _: ?*anyopaque) papyrus.HandlerError!void {
|
||||
var ctx = papyrus.getContext();
|
||||
pub fn buttonMouseOverListener(ctx: *papyrus.Context, node: papyrus.NodeHandle, _: ?*anyopaque) papyrus.HandlerError!void {
|
||||
var btn = ctx.getButton(node);
|
||||
if (btn.disabled == false) {
|
||||
btn.buttonState = .Hovered;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buttonMouseOffListener(node: papyrus.NodeHandle, _: ?*anyopaque) papyrus.HandlerError!void {
|
||||
var ctx = papyrus.getContext();
|
||||
pub fn buttonMouseOffListener(ctx: *papyrus.Context, node: papyrus.NodeHandle, _: ?*anyopaque) papyrus.HandlerError!void {
|
||||
var btn = ctx.getButton(node);
|
||||
if (btn.disabled == false) {
|
||||
btn.buttonState = .Normal;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buttonOnPressedEvent(node: papyrus.NodeHandle, eventType: papyrus.PressedType, _: ?*anyopaque) papyrus.HandlerError!void {
|
||||
var ctx = papyrus.getContext();
|
||||
pub fn buttonOnPressedEvent(ctx: *papyrus.Context, node: papyrus.NodeHandle, eventType: papyrus.PressedType, _: ?*anyopaque) papyrus.HandlerError!void {
|
||||
var btn = ctx.getButton(node);
|
||||
|
||||
if (eventType == .onPressed) {
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ pub const PhysicsRuntime = struct {
|
|||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
try zphysics.init(std.heap.c_allocator, .{});
|
||||
//try zphysics.init(allocator, .{});
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
|
|
|
|||
|
|
@ -209,8 +209,13 @@ pub const PlatformInstance = struct {
|
|||
if (self.imguiMouseConsumed and (event.type == sdl3.events.mouse_button_down or event.type == sdl3.events.mouse_button_up)) {
|
||||
shouldSkip = true;
|
||||
}
|
||||
if (!shouldSkip)
|
||||
if (!shouldSkip) {
|
||||
inputStack.routeEvent(converted);
|
||||
|
||||
if (event.type == sdl3.events.mouse_motion) {
|
||||
inputStack.setMousePosition(self.cursorPos.x, self.cursorPos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ pub fn resolve(self: *@This()) void {
|
|||
base = mul(core.zm.rotationX(self.pitch), base);
|
||||
base = mul(core.zm.rotationZ(self.roll), base);
|
||||
|
||||
var posRot: core.scene.SceneObjectPosRot = .{
|
||||
var posRot: core.scene.ScenePosRot = .{
|
||||
.rotation = scene.getRotation(),
|
||||
.position = scene.getPosition(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -375,6 +375,7 @@ pub const ParticleEmitter = struct {
|
|||
self.entity = core.Entity{ .handle = handle };
|
||||
|
||||
self.texture = rend.getTexture(&t_whiteName).?;
|
||||
core.engine_logs("wtf");
|
||||
self.mesh = rend.getMeshByName(&m_quad).?;
|
||||
|
||||
if (self.entity.fetch(core.Scene)) |scene| {
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ pub fn createBuffers(self: *@This()) !void {
|
|||
.props = 0,
|
||||
});
|
||||
|
||||
self.ssobUpload = self.device.createGPUTransferBuffer(&.{
|
||||
self.ssobUpload = rend.renderer.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = MaxObjectCount * @sizeOf(debug_vert.Scene),
|
||||
.props = 0,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|||
|
||||
const device = rend.context().device;
|
||||
|
||||
const MaxParticleCount = core.configVar(u32, "rend.particles.maxCount", 200_000);
|
||||
|
||||
self.ssboScene = device.createGPUBuffer(&.{
|
||||
.usage = .{
|
||||
.bufferusageGraphicsStorageRead = true,
|
||||
|
|
@ -32,7 +34,7 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|||
.props = 0,
|
||||
});
|
||||
|
||||
self.ssboSceneUpload = device.createGPUTransferBuffer(&.{
|
||||
self.ssboSceneUpload = rend.renderer.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = MaxParticleCount * @sizeOf(meshes_vert.Scene),
|
||||
.props = 0,
|
||||
|
|
@ -125,8 +127,6 @@ pub fn destroy(self: *@This()) void {
|
|||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
const MaxParticleCount = 10_000_000;
|
||||
|
||||
const assets = @import("assets");
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
|
|
|
|||
|
|
@ -101,13 +101,14 @@ pub fn uploadCubeFromPaths(self: *@This(), name: core.Name, paths: []const []con
|
|||
try core.assert(cubeList.items[0].size.x == cubeList.items[0].size.y);
|
||||
}
|
||||
|
||||
const transferBuffer = self.device.createGPUTransferBuffer(&.{
|
||||
const transferBufferSize = textureSize * textureSize * @sizeOf(u32);
|
||||
const transferBuffer = rend.renderer.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = textureSize * textureSize * @sizeOf(u32),
|
||||
.props = 0,
|
||||
});
|
||||
|
||||
defer self.device.releaseGPUTransferBuffer(transferBuffer);
|
||||
defer rend.renderer.releaseGPUTransferBuffer(transferBuffer, transferBufferSize);
|
||||
|
||||
const gtti: gpu.GPUTextureTransferInfo = .{
|
||||
.transfer_buffer = transferBuffer,
|
||||
|
|
@ -199,9 +200,10 @@ pub fn uploadTextureFromBytes(self: *@This(), name: *core.Name, opts: UploadText
|
|||
.num_levels = mipLevelCount,
|
||||
}));
|
||||
|
||||
const transferBuffer = self.device.createGPUTransferBuffer(&.{
|
||||
const transferBufferSize = opts.size.x * opts.size.y * @sizeOf(u32);
|
||||
const transferBuffer = rend.renderer.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = opts.size.x * opts.size.y * @sizeOf(u32),
|
||||
.size = transferBufferSize,
|
||||
.props = 0,
|
||||
});
|
||||
|
||||
|
|
@ -240,7 +242,7 @@ pub fn uploadTextureFromBytes(self: *@This(), name: *core.Name, opts: UploadText
|
|||
if (!cmd.submitGPUCommandBuffer()) {
|
||||
return error.CopyFailed;
|
||||
}
|
||||
self.device.releaseGPUTransferBuffer(transferBuffer);
|
||||
rend.renderer.releaseGPUTransferBuffer(transferBuffer, transferBufferSize);
|
||||
|
||||
const tex = try self.allocator.create(Texture);
|
||||
tex.* = .{
|
||||
|
|
@ -277,9 +279,10 @@ pub fn uploadTextureFromPath(self: *@This(), name: core.Name, path: []const u8)
|
|||
.num_levels = mipLevelCount,
|
||||
}));
|
||||
|
||||
const transferBuffer = self.device.createGPUTransferBuffer(&.{
|
||||
const transferBufferSize = png.size.x * png.size.y * @sizeOf(u32);
|
||||
const transferBuffer = rend.renderer.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = png.size.x * png.size.y * @sizeOf(u32),
|
||||
.size = transferBufferSize,
|
||||
.props = 0,
|
||||
});
|
||||
|
||||
|
|
@ -318,7 +321,7 @@ pub fn uploadTextureFromPath(self: *@This(), name: core.Name, path: []const u8)
|
|||
if (!cmd.submitGPUCommandBuffer()) {
|
||||
return error.CopyFailed;
|
||||
}
|
||||
self.device.releaseGPUTransferBuffer(transferBuffer);
|
||||
rend.renderer.releaseGPUTransferBuffer(transferBuffer, transferBufferSize);
|
||||
|
||||
const tex = try self.allocator.create(Texture);
|
||||
tex.* = .{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ pub const MeshPool = struct {
|
|||
device: *gpu.GPUDevice,
|
||||
|
||||
destroyList: std.ArrayList(*gpu.GPUTransferBuffer),
|
||||
destroyListSizes: std.ArrayList(usize),
|
||||
|
||||
installedMeshes: std.AutoHashMapUnmanaged(u32, meshes.IndexedMesh) = .{},
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ pub const MeshPool = struct {
|
|||
.vertexSpans = try core.MergedSpans.init(allocator, settings.vertexCount),
|
||||
.meshUpdates = try core.RingQueue(rend.MeshUpdate).init(allocator, 128),
|
||||
.destroyList = std.ArrayList(*gpu.GPUTransferBuffer).init(allocator),
|
||||
.destroyListSizes = std.ArrayList(usize).init(allocator),
|
||||
.device = device,
|
||||
};
|
||||
|
||||
|
|
@ -67,11 +69,12 @@ pub const MeshPool = struct {
|
|||
pub fn onUploadCleanup(p: *anyopaque) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(p));
|
||||
|
||||
for (self.destroyList.items) |transferBuffer| {
|
||||
self.device.releaseGPUTransferBuffer(transferBuffer);
|
||||
for (self.destroyList.items, 0..) |transferBuffer, i| {
|
||||
rend.renderer.releaseGPUTransferBuffer(transferBuffer, self.destroyListSizes.items[i]);
|
||||
}
|
||||
|
||||
self.destroyList.clearRetainingCapacity();
|
||||
self.destroyListSizes.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub const JointMap = std.AutoHashMapUnmanaged(u32, u32);
|
||||
|
|
@ -170,8 +173,10 @@ pub const MeshPool = struct {
|
|||
uploadSlice: []const T,
|
||||
) !core.Span {
|
||||
const newSpan = try mergedSpans.allocate(@intCast(uploadSlice.len));
|
||||
const upload = self.device.createGPUTransferBuffer(&.{ .usage = .transferbufferusageUpload, .size = @sizeOf(T) * newSpan.size, .props = 0 });
|
||||
const uploadSize = @sizeOf(T) * newSpan.size;
|
||||
const upload = rend.renderer.createGPUTransferBuffer(&.{ .usage = .transferbufferusageUpload, .size = uploadSize, .props = 0 });
|
||||
try self.destroyList.append(upload);
|
||||
try self.destroyListSizes.append(uploadSize);
|
||||
|
||||
var mappedSlice: []T = undefined;
|
||||
mappedSlice.ptr = @ptrCast(@alignCast(self.device.mapGPUTransferBuffer(upload, false)));
|
||||
|
|
@ -207,6 +212,7 @@ pub const MeshPool = struct {
|
|||
|
||||
self.installedMeshes.deinit(self.allocator);
|
||||
self.destroyList.deinit();
|
||||
self.destroyListSizes.deinit();
|
||||
self.indexSpans.deinit();
|
||||
self.meshUpdates.deinit();
|
||||
self.vertexSpans.deinit();
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ pub const Renderer = struct {
|
|||
.props = 0,
|
||||
});
|
||||
|
||||
self.ssboSceneUpload = self.device.createGPUTransferBuffer(&.{
|
||||
self.ssboSceneUpload = createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = MaxObjectCount * @sizeOf(meshes_vert.Scene),
|
||||
.props = 0,
|
||||
|
|
@ -638,7 +638,7 @@ pub const Renderer = struct {
|
|||
.props = 0,
|
||||
});
|
||||
|
||||
self.ssboAnimationUpload = self.device.createGPUTransferBuffer(&.{
|
||||
self.ssboAnimationUpload = createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = MaxObjectCount * @sizeOf(meshes_vert.BoneTransform),
|
||||
.props = 0,
|
||||
|
|
@ -1284,6 +1284,16 @@ pub fn getTexture(name: *core.Name) ?*rend.Texture {
|
|||
return context().textureList.map.get(name.handle());
|
||||
}
|
||||
|
||||
pub fn createGPUTransferBuffer(createInfo: *const gpu.GPUTransferBufferCreateInfo) *gpu.GPUTransferBuffer {
|
||||
core.MemoryTracker.MTAddUntrackedAllocation(@intCast(createInfo.size));
|
||||
return context().device.createGPUTransferBuffer(createInfo);
|
||||
}
|
||||
|
||||
pub fn releaseGPUTransferBuffer(transferBuffer: *gpu.GPUTransferBuffer, size: usize) void {
|
||||
core.MemoryTracker.MTRemoveAllocation(size);
|
||||
context().device.releaseGPUTransferBuffer(transferBuffer);
|
||||
}
|
||||
|
||||
pub const RendererState = struct {
|
||||
copyPass: ?*gpu.GPURenderPass = null,
|
||||
pass: ?*gpu.GPURenderPass = null,
|
||||
|
|
|
|||
|
|
@ -70,12 +70,13 @@ pub fn createNoiseTexture(self: *@This()) !void {
|
|||
});
|
||||
self.noiseTexture = ctx.device.createGPUTexture(&tci);
|
||||
|
||||
const transferBuffer = ctx.device.createGPUTransferBuffer(&.{
|
||||
const transferBufferSize = textureSize * textureSize * @sizeOf(f32) * 4;
|
||||
const transferBuffer = rend.renderer.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = textureSize * textureSize * @sizeOf(f32) * 4,
|
||||
.size = transferBufferSize,
|
||||
.props = 0,
|
||||
});
|
||||
defer ctx.device.releaseGPUTransferBuffer(transferBuffer);
|
||||
defer rend.renderer.releaseGPUTransferBuffer(transferBuffer, transferBufferSize);
|
||||
|
||||
const gtti: gpu.GPUTextureTransferInfo = .{
|
||||
.transfer_buffer = transferBuffer,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ textRenderers: std.AutoHashMapUnmanaged(*papyrus.Context, *TextRenderer) = .{},
|
|||
|
||||
reloadingShaders: bool = false,
|
||||
|
||||
lastEventsCount: u64 = 0,
|
||||
|
||||
const DrawCommand = union(enum(u8)) {
|
||||
rect: struct {
|
||||
ssboIndex: u32,
|
||||
|
|
@ -66,7 +68,7 @@ const SsboBuffer = struct {
|
|||
.props = 0,
|
||||
};
|
||||
|
||||
self.staging = device.createGPUTransferBuffer(&bci);
|
||||
self.staging = rend.renderer.createGPUTransferBuffer(&bci);
|
||||
}
|
||||
|
||||
core.engine_log("buffer created elementSize: {d} count: {d}", .{ elementSize, count });
|
||||
|
|
@ -129,9 +131,36 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|||
}
|
||||
|
||||
pub fn tick(self: *@This(), dt: f64) void {
|
||||
const inputStack = core.getInputStack();
|
||||
self.screenContext.setCursorLocation(.{
|
||||
.x = @floatCast(inputStack.mousePosition.x),
|
||||
.y = @floatCast(inputStack.mousePosition.y),
|
||||
});
|
||||
|
||||
self.screenContext.tick(dt) catch {
|
||||
core.engine_errs("unable to tick papyrus");
|
||||
};
|
||||
|
||||
self.screenContext.pushDebugText("fps: {d:.2} ({d:.4}ms)", .{ 1 / core.getEngine().averageFrameTime, core.getEngine().averageFrameTime }) catch {};
|
||||
|
||||
if (core.MemoryTracker.MTGet()) |tracker| {
|
||||
self.screenContext.pushDebugText("memory used: {d:.4} MiB in {d} allocations {d} events per frame", .{
|
||||
@as(f64, @floatFromInt(tracker.totalAllocSize)) / 1024 / 1024,
|
||||
tracker.allocationsCount,
|
||||
tracker.eventsCount - self.lastEventsCount,
|
||||
}) catch {};
|
||||
|
||||
self.screenContext.pushDebugText("untracked allocations: {d:.4} MiB in {d} allocations", .{
|
||||
@as(f64, @floatFromInt(tracker.untrackedAllocationsSize)) / 1024 / 1024,
|
||||
tracker.untrackedAllocationsCount,
|
||||
}) catch {};
|
||||
|
||||
self.screenContext.pushDebugText("total {d:.4} MiB", .{
|
||||
@as(f64, @floatFromInt(tracker.getTotalMemoryUsed())) / 1024 / 1024,
|
||||
}) catch {};
|
||||
|
||||
self.lastEventsCount = tracker.eventsCount;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup(self: *@This()) !void {
|
||||
|
|
|
|||
|
|
@ -371,6 +371,10 @@ pub const TextRenderer = struct {
|
|||
item.destroy(self.allocator);
|
||||
}
|
||||
|
||||
for (self.deadList.items) |item| {
|
||||
item.destroy(self.allocator);
|
||||
}
|
||||
|
||||
self.geo.destroy();
|
||||
self.linearBuffers.deinit(self.allocator);
|
||||
self.assignedBuffers.deinit(self.allocator);
|
||||
|
|
@ -383,7 +387,9 @@ pub const TextMeshBuffer = struct {
|
|||
indexBuffer: *gpu.GPUBuffer = undefined,
|
||||
vertexBuffer: *gpu.GPUBuffer = undefined,
|
||||
indexTransferBuffer: *gpu.GPUTransferBuffer = undefined,
|
||||
indexTransferBufferSize: usize,
|
||||
vertexTransferBuffer: *gpu.GPUTransferBuffer = undefined,
|
||||
vertexTransferBufferSize: usize,
|
||||
indexCount: u32 = 0,
|
||||
isSDF: bool = true,
|
||||
fontHandle: u32 = 0,
|
||||
|
|
@ -395,15 +401,18 @@ pub const TextMeshBuffer = struct {
|
|||
const vertexBufferCount: u32 = maxChars * 4;
|
||||
const indexBufferCount: u32 = maxChars * 6;
|
||||
|
||||
self.vertexTransferBufferSize = vertexBufferCount * @sizeOf(TextMeshVertex);
|
||||
self.indexTransferBufferSize = indexBufferCount * @sizeOf(u32);
|
||||
|
||||
const ctx = rend.context();
|
||||
|
||||
self.vertexTransferBuffer = ctx.device.createGPUTransferBuffer(&.{
|
||||
self.vertexTransferBuffer = rend.renderer.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = vertexBufferCount * @sizeOf(TextMeshVertex),
|
||||
.props = 0,
|
||||
});
|
||||
|
||||
self.indexTransferBuffer = ctx.device.createGPUTransferBuffer(&.{
|
||||
self.indexTransferBuffer = rend.renderer.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = indexBufferCount * @sizeOf(u32),
|
||||
.props = 0,
|
||||
|
|
@ -429,8 +438,8 @@ pub const TextMeshBuffer = struct {
|
|||
|
||||
ctx.device.releaseGPUBuffer(self.indexBuffer);
|
||||
ctx.device.releaseGPUBuffer(self.vertexBuffer);
|
||||
ctx.device.releaseGPUTransferBuffer(self.indexTransferBuffer);
|
||||
ctx.device.releaseGPUTransferBuffer(self.vertexTransferBuffer);
|
||||
rend.renderer.releaseGPUTransferBuffer(self.indexTransferBuffer, self.indexTransferBufferSize);
|
||||
rend.renderer.releaseGPUTransferBuffer(self.vertexTransferBuffer, self.vertexTransferBufferSize);
|
||||
|
||||
allocator.destroy(self);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,26 @@ pub fn windowOpen(entry: *igu.MenuEntry, dt: f64) void {
|
|||
rend.setSkyboxTexture(name.utf8());
|
||||
}
|
||||
}
|
||||
|
||||
ig.textFmt("textures", .{}) catch unreachable;
|
||||
ig.separator();
|
||||
|
||||
{
|
||||
var iter = rend.context().textureList.map.iterator();
|
||||
while (iter.next()) |n| {
|
||||
ig.textFmt("{s}", .{n.value_ptr.*.name.utf8()}) catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
ig.textFmt("meshes", .{}) catch unreachable;
|
||||
ig.separator();
|
||||
|
||||
{
|
||||
var iter = rend.context().meshPool.installedMeshes.iterator();
|
||||
while (iter.next()) |n| {
|
||||
ig.textFmt("{s}", .{n.value_ptr.*.name.utf8()}) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
ig.end();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ windowOpen: bool = true,
|
|||
spawnFuncs: std.ArrayListUnmanaged(SpawnEntry) = .{},
|
||||
spawnPosition: core.Vectorf = .{},
|
||||
spawnRotation: core.Rotation = .{},
|
||||
spawnTarget: ?u32 = null,
|
||||
|
||||
pub const SpawnEntry = struct {
|
||||
typeName: []const u8,
|
||||
|
|
@ -28,16 +29,23 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|||
}
|
||||
|
||||
pub fn objectSpawnOptions(self: *@This()) void {
|
||||
for (self.spawnFuncs.items) |entry| {
|
||||
for (self.spawnFuncs.items, 0..) |entry, i| {
|
||||
if (ig.smallButton(entry.typeName.ptr)) {
|
||||
if (entry.spawnFunc(self.objectList)) |new| {
|
||||
const entity = new.vtable.getEntity.?(new.ptr);
|
||||
self.spawnTarget = @intCast(i);
|
||||
if (!entry.opts.absoluteSpawnposition) {
|
||||
self.prepareEntity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.spawnTarget) |target| {
|
||||
if (target >= self.spawnFuncs.items.len) {
|
||||
self.spawnTarget = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawnedObjectsList(self: *@This()) void {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
allocator: std.mem.Allocator,
|
||||
|
||||
selectedObjectToSpawn: ?core.Name = null,
|
||||
|
||||
spawnedObjects: std.ArrayListUnmanaged(core.Entity) = .{},
|
||||
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "extras.ObjectSystemSpawner");
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{ .allocator = allocator };
|
||||
|
||||
if (core.getEngineObject(igu.TopBar)) |topbar| {
|
||||
topbar.addMenuObject(self, "New Object Spawner") catch {};
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// spawns an arbitrary object, no type acquisition here
|
||||
pub fn spawnObjectAt(self: *@This(), spawnPosRot: core.ScenePosRot) ?core.Entity {
|
||||
if (self.selectedObjectToSpawn == null)
|
||||
return null;
|
||||
|
||||
const objectSystem: *core.GameObjectSystem = core.get(core.GameObjectSystem);
|
||||
const r = objectSystem.spawnObjectFromTableByName(&self.selectedObjectToSpawn.?, .{ .posRot = spawnPosRot }) catch return null;
|
||||
self.spawnedObjects.append(self.allocator, r.entity) catch return null;
|
||||
return r.entity;
|
||||
}
|
||||
|
||||
pub fn windowOpen(entry: *igu.MenuEntry, dt: f64) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(entry.ctx));
|
||||
_ = dt;
|
||||
|
||||
const object = core.getEngineObject(core.GameObjectSystem).?;
|
||||
|
||||
if (ig.begin("ObjectSystemSpawner", null, .{})) {
|
||||
if (self.selectedObjectToSpawn) |*selected| {
|
||||
ig.textf("now spawning : {s}", .{selected.utf8()});
|
||||
} else {
|
||||
ig.textf("click an object to spawn", .{});
|
||||
}
|
||||
|
||||
var buf: [256]u8 = undefined;
|
||||
var i = object.objectDefinitions.iterator();
|
||||
while (i.next()) |v| {
|
||||
const x = std.fmt.bufPrintZ(&buf, "spawn##{d}", .{v.value_ptr.objectBaseName.handle()}) catch unreachable;
|
||||
ig.textf("{s}", .{v.value_ptr.objectBaseName.utf8()});
|
||||
ig.sameLine(0, 10);
|
||||
if (ig.smallButton(x)) {
|
||||
self.selectedObjectToSpawn = v.value_ptr.objectBaseName;
|
||||
}
|
||||
}
|
||||
|
||||
ig.separator();
|
||||
var destroyed: ?usize = null;
|
||||
for (self.spawnedObjects.items, 0..) |entity, j| {
|
||||
const x = std.fmt.bufPrintZ(&buf, "delete {d}", .{entity.handle.index}) catch unreachable;
|
||||
if (ig.smallButton(x)) {
|
||||
entity.destroy();
|
||||
destroyed = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyed) |destroyedIndex| {
|
||||
_ = self.spawnedObjects.orderedRemove(destroyedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
ig.end();
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.spawnedObjects.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
const backlog = @import("Backlog");
|
||||
const core = backlog.core;
|
||||
const rend = backlog.rend;
|
||||
const ig = backlog.imgui.api;
|
||||
const igu = backlog.imgui.utils;
|
||||
const std = @import("std");
|
||||
|
|
@ -1,11 +1,27 @@
|
|||
pub const FpCamera = @import("gameplay/FpCamera.zig");
|
||||
pub const pawns = @import("gameplay/pawns.zig");
|
||||
pub const games = @import("gameplay/games.zig");
|
||||
|
||||
pub const inputDebugger = @import("debuggers/inputDebugger.zig");
|
||||
pub const ObjectSpawner = @import("debuggers/objectSpawner.zig");
|
||||
pub const ObjectSystemSpawner = @import("debuggers/objectSystemSpawner.zig");
|
||||
pub const RendererDebug = @import("debuggers/RendererDebug.zig");
|
||||
pub const EngineTool = @import("debuggers/EngineTool.zig");
|
||||
pub const PhysicsObjectList = @import("debuggers/PhysicsObjectList.zig");
|
||||
pub const ParticleDebugger = @import("debuggers/ParticleDebugger.zig");
|
||||
|
||||
pub const browser = @import("debuggers/fileBrowser.zig");
|
||||
|
||||
pub fn setup() !void {
|
||||
_ = try core.createObject(games.GameList, .{});
|
||||
}
|
||||
|
||||
pub const GameModeMessage = games.GameModeMessage;
|
||||
pub const GameModeState = games.GameModeState;
|
||||
pub const getGame = games.getGame;
|
||||
pub const beginGame = games.beginGame;
|
||||
pub const endGame = games.endGame;
|
||||
pub const unloadGame = games.unloadGame;
|
||||
|
||||
const std = @import("std");
|
||||
const backlog = @import("Backlog");
|
||||
const core = backlog.core;
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
// creates a
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
// games are a
|
||||
|
||||
pub const GameInterface = struct {
|
||||
ptr: *anyopaque,
|
||||
beginPlayFn: *const fn (*anyopaque) void,
|
||||
endPlayFn: *const fn (*anyopaque) void,
|
||||
|
||||
gameName: core.Name,
|
||||
tags: std.ArrayListUnmanaged(core.Name) = .{},
|
||||
|
||||
pub fn beginPlay(self: @This()) void {
|
||||
self.beginPlayFn(self.ptr);
|
||||
}
|
||||
|
||||
pub fn endPlay(self: @This()) void {
|
||||
self.endPlayFn(self.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
pub const GameList = struct {
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "extras.GameList");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
games: std.StringHashMapUnmanaged(*GameInterface) = .{},
|
||||
gamesByTag: std.AutoHashMapUnmanaged(u32, GameInterfaceList) = .{},
|
||||
gamesLinear: GameInterfaceList = .{},
|
||||
|
||||
const GameInterfaceList = std.ArrayListUnmanaged(*GameInterface);
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn registerGame(self: *@This(), gameName: []const u8, p: *anyopaque, comptime InnerType: type) !void {
|
||||
const interface = try self.allocator.create(GameInterface);
|
||||
|
||||
interface.* = .{
|
||||
.ptr = p,
|
||||
.beginPlayFn = InnerType.beginPlay,
|
||||
.endPlayFn = InnerType.endPlay,
|
||||
.gameName = core.MakeName(gameName),
|
||||
};
|
||||
|
||||
try self.games.put(self.allocator, gameName, interface);
|
||||
try self.gamesLinear.append(self.allocator, interface);
|
||||
}
|
||||
|
||||
pub fn setGameTag(self: *@This(), gameName: []const u8, name: []const u8) void {
|
||||
var n = core.MakeName(name);
|
||||
(self.gamesByTag.getOrPut(gameName) catch unreachable).append(n.handle()) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn removeTag(self: *@This(), game: []const u8, tag: []const u8) void {
|
||||
var tagName = core.MakeName(tag);
|
||||
var gameName = core.MakeName(game);
|
||||
|
||||
if (self.gamesByTag.getPtr(tagName.handle())) |list| {
|
||||
for (list.items) |i| {
|
||||
if (i.gameName.eql(&gameName)) {
|
||||
list.swapRemove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
var iter = self.games.valueIterator();
|
||||
|
||||
while (iter.next()) |i| {
|
||||
self.allocator.destroy(i.*);
|
||||
}
|
||||
|
||||
self.gamesLinear.deinit(self.allocator);
|
||||
self.gamesByTag.deinit(self.allocator);
|
||||
self.games.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn beginGame(name: []const u8) void {
|
||||
getGame(name).beginPlay();
|
||||
}
|
||||
|
||||
pub fn endGame(name: []const u8) void {
|
||||
getGame(name).endPlay();
|
||||
}
|
||||
|
||||
pub fn getGame(name: []const u8) *GameInterface {
|
||||
core.engine_log("getting Game {s}", .{name});
|
||||
return core.EngineObject(GameList).get().games.get(name).?;
|
||||
}
|
||||
|
||||
pub fn addGame(name: []const u8, p: *anyopaque, comptime T: type) !void {
|
||||
core.engine_log("setting Game {s}", .{name});
|
||||
try core.EngineObject(GameList).get().registerGame(name, p, T);
|
||||
}
|
||||
|
||||
pub const GameModeState = enum {
|
||||
dead,
|
||||
prepare,
|
||||
playing,
|
||||
paused,
|
||||
};
|
||||
|
||||
pub const GameModeMessage = struct {
|
||||
gameModeName: []const u8,
|
||||
gameModeRef: ?*anyopaque = null,
|
||||
gameModeStatus: GameModeState = .dead,
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const backlog = @import("Backlog");
|
||||
const core = backlog.core;
|
||||
|
|
@ -1 +0,0 @@
|
|||
// this is an implementation of
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false;
|
||||
|
||||
// am i good to always have enet as static?
|
||||
const enet = if (static_build and false) b.addStaticLibrary(.{
|
||||
.name = "enet_c",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}) else b.addSharedLibrary(.{
|
||||
.name = "enet_c",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
enet.linkLibC();
|
||||
|
||||
enet.addCSourceFiles(.{
|
||||
.root = b.path("enet-1.3.18"),
|
||||
.files = &.{
|
||||
"callbacks.c",
|
||||
"compress.c",
|
||||
"host.c",
|
||||
"list.c",
|
||||
"packet.c",
|
||||
"peer.c",
|
||||
"protocol.c",
|
||||
"unix.c",
|
||||
"win32.c",
|
||||
},
|
||||
.flags = &.{"-DHAS_OFFSETOF=1"},
|
||||
});
|
||||
|
||||
enet.addIncludePath(b.path("enet-1.3.18/include"));
|
||||
|
||||
// Platform-specific configuration
|
||||
switch (target.result.os.tag) {
|
||||
.windows => {
|
||||
enet.linkSystemLibrary("ws2_32");
|
||||
enet.linkSystemLibrary("winmm");
|
||||
},
|
||||
.linux, .macos => {
|
||||
// Unix platforms - no additional libraries needed
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
b.installArtifact(enet);
|
||||
|
||||
const mod = b.addModule("enet", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("src/enet.zig"),
|
||||
.link_libc = true,
|
||||
});
|
||||
|
||||
mod.linkLibrary(enet);
|
||||
mod.addIncludePath(b.path("enet-1.3.18/include/"));
|
||||
|
||||
const test_step = b.step("test", "run unit tests for enet");
|
||||
const tests = b.addTest(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("src/tests.zig"),
|
||||
.link_libc = true,
|
||||
});
|
||||
tests.root_module.addImport("enet", mod);
|
||||
tests.root_module.addIncludePath(b.path("enet-1.3.18/include/"));
|
||||
const runArtifact = b.addRunArtifact(tests);
|
||||
test_step.dependOn(&runArtifact.step);
|
||||
|
||||
// Test server executable
|
||||
const test_server = b.addExecutable(.{
|
||||
.name = "test-server",
|
||||
.root_source_file = b.path("src/test_server.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
test_server.root_module.addImport("enet", mod);
|
||||
test_server.linkLibC();
|
||||
|
||||
// Test client executable
|
||||
const test_client = b.addExecutable(.{
|
||||
.name = "test-client",
|
||||
.root_source_file = b.path("src/test_client.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
test_client.root_module.addImport("enet", mod);
|
||||
test_client.linkLibC();
|
||||
|
||||
// Install test programs
|
||||
const install_test_server = b.addInstallArtifact(test_server, .{});
|
||||
const install_test_client = b.addInstallArtifact(test_client, .{});
|
||||
|
||||
// Steps to run test programs individually
|
||||
const run_test_server = b.addRunArtifact(test_server);
|
||||
const run_test_client = b.addRunArtifact(test_client);
|
||||
|
||||
const run_server_step = b.step("run-test-server", "Run the ENet test server");
|
||||
run_server_step.dependOn(&run_test_server.step);
|
||||
|
||||
const run_client_step = b.step("run-test-client", "Run the ENet test client");
|
||||
run_client_step.dependOn(&run_test_client.step);
|
||||
|
||||
// Loopback test that runs both server and client
|
||||
const run_loopback_step = b.step("run-test-loopback", "Run ENet loopback test (server + client)");
|
||||
|
||||
// Create a custom step for the loopback test
|
||||
const loopback_test = LoopbackTestStep.create(b, test_server, test_client);
|
||||
run_loopback_step.dependOn(&loopback_test.step);
|
||||
|
||||
// Build step for test programs
|
||||
const build_tests_step = b.step("build-tests", "Build test server and client programs");
|
||||
build_tests_step.dependOn(&install_test_server.step);
|
||||
build_tests_step.dependOn(&install_test_client.step);
|
||||
}
|
||||
|
||||
// Custom step to run server and client concurrently for loopback testing
|
||||
const LoopbackTestStep = struct {
|
||||
step: std.Build.Step,
|
||||
builder: *std.Build,
|
||||
server_exe: *std.Build.Step.Compile,
|
||||
client_exe: *std.Build.Step.Compile,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn create(builder: *std.Build, server_exe: *std.Build.Step.Compile, client_exe: *std.Build.Step.Compile) *Self {
|
||||
const self = builder.allocator.create(Self) catch @panic("OOM");
|
||||
self.* = Self{
|
||||
.step = std.Build.Step.init(.{
|
||||
.id = .custom,
|
||||
.name = "loopback-test",
|
||||
.owner = builder,
|
||||
.makeFn = make,
|
||||
}),
|
||||
.builder = builder,
|
||||
.server_exe = server_exe,
|
||||
.client_exe = client_exe,
|
||||
};
|
||||
|
||||
self.step.dependOn(&server_exe.step);
|
||||
self.step.dependOn(&client_exe.step);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
|
||||
const self: *Self = @fieldParentPtr("step", step);
|
||||
const allocator = self.builder.allocator;
|
||||
|
||||
std.debug.print("Running ENet loopback test...\n", .{});
|
||||
|
||||
// Get the executable paths
|
||||
const server_path = self.server_exe.getEmittedBin().getPath(self.builder);
|
||||
const client_path = self.client_exe.getEmittedBin().getPath(self.builder);
|
||||
|
||||
// Start the server process
|
||||
std.debug.print("Starting test server...\n", .{});
|
||||
var server_process = std.process.Child.init(&[_][]const u8{server_path}, allocator);
|
||||
server_process.stdout_behavior = .Pipe;
|
||||
server_process.stderr_behavior = .Pipe;
|
||||
|
||||
try server_process.spawn();
|
||||
defer {
|
||||
_ = server_process.kill() catch {};
|
||||
}
|
||||
|
||||
// Give server time to start
|
||||
std.time.sleep(1 * std.time.ns_per_s);
|
||||
|
||||
// Start the client process
|
||||
std.debug.print("Starting test client...\n", .{});
|
||||
var client_process = std.process.Child.init(&[_][]const u8{client_path}, allocator);
|
||||
client_process.stdout_behavior = .Pipe;
|
||||
client_process.stderr_behavior = .Pipe;
|
||||
|
||||
try client_process.spawn();
|
||||
|
||||
// Wait for client to complete
|
||||
const client_result = try client_process.wait();
|
||||
|
||||
// Give server a moment to process the quit message and shut down naturally
|
||||
std.time.sleep(2 * std.time.ns_per_s);
|
||||
|
||||
// Check if server is still running and terminate if needed
|
||||
const server_result = server_process.wait() catch {
|
||||
std.debug.print("Server still running, terminating...\n", .{});
|
||||
_ = server_process.kill() catch {};
|
||||
return;
|
||||
};
|
||||
|
||||
// Read and display output
|
||||
if (client_process.stdout) |stdout| {
|
||||
const client_output = try stdout.reader().readAllAlloc(allocator, 8192);
|
||||
defer allocator.free(client_output);
|
||||
std.debug.print("\n=== CLIENT OUTPUT ===\n{s}\n", .{client_output});
|
||||
}
|
||||
|
||||
if (server_process.stdout) |stdout| {
|
||||
const server_output = try stdout.reader().readAllAlloc(allocator, 8192);
|
||||
defer allocator.free(server_output);
|
||||
std.debug.print("\n=== SERVER OUTPUT ===\n{s}\n", .{server_output});
|
||||
}
|
||||
|
||||
std.debug.print("\n=== LOOPBACK TEST RESULTS ===\n", .{});
|
||||
std.debug.print("Client exit code: {}\n", .{client_result});
|
||||
std.debug.print("Server exit code: {}\n", .{server_result});
|
||||
|
||||
if (client_result == .Exited and client_result.Exited == 0 and
|
||||
server_result == .Exited and server_result.Exited == 0)
|
||||
{
|
||||
std.debug.print("Loopback test PASSED!\n", .{});
|
||||
} else {
|
||||
std.debug.print("Loopback test FAILED!\n", .{});
|
||||
return error.TestFailed;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
on: [push, pull_request]
|
||||
|
||||
name: CMake
|
||||
|
||||
jobs:
|
||||
cmake-build:
|
||||
name: CMake ${{ matrix.os }} ${{ matrix.build_type }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
|
||||
build_type: ["Debug", "Release"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{ matrix.build_type }}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
# Potential build directories
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
[Bb]in/
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
[Oo]bj/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
[Ww][Ii][Nn]32/
|
||||
bld/
|
||||
build/
|
||||
builds/
|
||||
out/
|
||||
x64/
|
||||
x86/
|
||||
|
||||
# VS
|
||||
.vs/
|
||||
.vscode/
|
||||
!.vscode/extensions.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
|
||||
# CMake
|
||||
_deps
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeLists.txt.user
|
||||
CMakeScripts
|
||||
CMakeUserPresets.json
|
||||
CTestTestfile.cmake
|
||||
cmake_install.cmake
|
||||
compile_commands.json
|
||||
install_manifest.txt
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
cmake_minimum_required(VERSION 2.8.12...3.20)
|
||||
|
||||
project(enet)
|
||||
|
||||
# The "configure" step.
|
||||
include(CheckFunctionExists)
|
||||
include(CheckStructHasMember)
|
||||
include(CheckTypeSize)
|
||||
check_function_exists("fcntl" HAS_FCNTL)
|
||||
check_function_exists("poll" HAS_POLL)
|
||||
check_function_exists("getaddrinfo" HAS_GETADDRINFO)
|
||||
check_function_exists("getnameinfo" HAS_GETNAMEINFO)
|
||||
check_function_exists("gethostbyname_r" HAS_GETHOSTBYNAME_R)
|
||||
check_function_exists("gethostbyaddr_r" HAS_GETHOSTBYADDR_R)
|
||||
check_function_exists("inet_pton" HAS_INET_PTON)
|
||||
check_function_exists("inet_ntop" HAS_INET_NTOP)
|
||||
check_c_source_compiles("
|
||||
#include <stddef.h>
|
||||
struct S { int a; double b; };
|
||||
int main() {
|
||||
return (int)offsetof(struct S, b);
|
||||
}
|
||||
" HAS_OFFSETOF)
|
||||
check_struct_has_member("struct msghdr" "msg_flags" "sys/types.h;sys/socket.h" HAS_MSGHDR_FLAGS)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES "sys/types.h" "sys/socket.h")
|
||||
check_type_size("socklen_t" HAS_SOCKLEN_T BUILTIN_TYPES_ONLY)
|
||||
unset(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
if(MSVC)
|
||||
add_definitions(-W3)
|
||||
else()
|
||||
add_definitions(-Wno-error)
|
||||
endif()
|
||||
|
||||
if(HAS_FCNTL)
|
||||
add_definitions(-DHAS_FCNTL=1)
|
||||
endif()
|
||||
if(HAS_POLL)
|
||||
add_definitions(-DHAS_POLL=1)
|
||||
endif()
|
||||
if(HAS_GETNAMEINFO)
|
||||
add_definitions(-DHAS_GETNAMEINFO=1)
|
||||
endif()
|
||||
if(HAS_GETADDRINFO)
|
||||
add_definitions(-DHAS_GETADDRINFO=1)
|
||||
endif()
|
||||
if(HAS_GETHOSTBYNAME_R)
|
||||
add_definitions(-DHAS_GETHOSTBYNAME_R=1)
|
||||
endif()
|
||||
if(HAS_GETHOSTBYADDR_R)
|
||||
add_definitions(-DHAS_GETHOSTBYADDR_R=1)
|
||||
endif()
|
||||
if(HAS_INET_PTON)
|
||||
add_definitions(-DHAS_INET_PTON=1)
|
||||
endif()
|
||||
if(HAS_INET_NTOP)
|
||||
add_definitions(-DHAS_INET_NTOP=1)
|
||||
endif()
|
||||
if(HAS_OFFSETOF)
|
||||
add_definitions(-DHAS_OFFSETOF=1)
|
||||
endif()
|
||||
if(HAS_MSGHDR_FLAGS)
|
||||
add_definitions(-DHAS_MSGHDR_FLAGS=1)
|
||||
endif()
|
||||
if(HAS_SOCKLEN_T)
|
||||
add_definitions(-DHAS_SOCKLEN_T=1)
|
||||
endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
set(INCLUDE_FILES_PREFIX include/enet)
|
||||
set(INCLUDE_FILES
|
||||
${INCLUDE_FILES_PREFIX}/callbacks.h
|
||||
${INCLUDE_FILES_PREFIX}/enet.h
|
||||
${INCLUDE_FILES_PREFIX}/list.h
|
||||
${INCLUDE_FILES_PREFIX}/protocol.h
|
||||
${INCLUDE_FILES_PREFIX}/time.h
|
||||
${INCLUDE_FILES_PREFIX}/types.h
|
||||
${INCLUDE_FILES_PREFIX}/unix.h
|
||||
${INCLUDE_FILES_PREFIX}/utility.h
|
||||
${INCLUDE_FILES_PREFIX}/win32.h
|
||||
)
|
||||
|
||||
set(SOURCE_FILES
|
||||
callbacks.c
|
||||
compress.c
|
||||
host.c
|
||||
list.c
|
||||
packet.c
|
||||
peer.c
|
||||
protocol.c
|
||||
unix.c
|
||||
win32.c)
|
||||
|
||||
source_group(include FILES ${INCLUDE_FILES})
|
||||
source_group(source FILES ${SOURCE_FILES})
|
||||
|
||||
if(WIN32 AND BUILD_SHARED_LIBS AND (MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
add_definitions(-DENET_DLL=1)
|
||||
add_definitions(-DENET_BUILDING_LIB)
|
||||
endif()
|
||||
|
||||
add_library(enet
|
||||
${INCLUDE_FILES}
|
||||
${SOURCE_FILES}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(enet winmm ws2_32)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS enet
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/enet
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
ENet 1.3.18 (April 14, 2024):
|
||||
|
||||
* Packet sending performance improvements
|
||||
* MTU negotiation fixes
|
||||
* Checksum alignment fix
|
||||
* No more dynamic initialization of checksum table
|
||||
* ENET_SOCKOPT_TTL
|
||||
* Other miscellaneous small improvements
|
||||
|
||||
ENet 1.3.17 (November 15, 2020):
|
||||
|
||||
* fixes for sender getting too far ahead of receiver that can cause instability with reliable packets
|
||||
|
||||
ENet 1.3.16 (September 8, 2020):
|
||||
|
||||
* fix bug in unreliable fragment queuing
|
||||
* use single output queue for reliable and unreliable packets for saner ordering
|
||||
* revert experimental throttle changes that were less stable than prior algorithm
|
||||
|
||||
ENet 1.3.15 (April 20, 2020):
|
||||
|
||||
* quicker RTT initialization
|
||||
* use fractional precision for RTT calculations
|
||||
* fixes for packet throttle with low RTT variance
|
||||
* miscellaneous socket bug fixes
|
||||
|
||||
ENet 1.3.14 (January 27, 2019):
|
||||
|
||||
* bug fix for enet_peer_disconnect_later()
|
||||
* use getaddrinfo and getnameinfo where available
|
||||
* miscellaneous cleanups
|
||||
|
||||
ENet 1.3.13 (April 30, 2015):
|
||||
|
||||
* miscellaneous bug fixes
|
||||
* added premake and cmake support
|
||||
* miscellaneous documentation cleanups
|
||||
|
||||
ENet 1.3.12 (April 24, 2014):
|
||||
|
||||
* added maximumPacketSize and maximumWaitingData fields to ENetHost to limit the amount of
|
||||
data waiting to be delivered on a peer (beware that the default maximumPacketSize is
|
||||
32MB and should be set higher if desired as should maximumWaitingData)
|
||||
|
||||
ENet 1.3.11 (December 26, 2013):
|
||||
|
||||
* allow an ENetHost to connect to itself
|
||||
* fixed possible bug with disconnect notifications during connect attempts
|
||||
* fixed some preprocessor definition bugs
|
||||
|
||||
ENet 1.3.10 (October 23, 2013);
|
||||
|
||||
* doubled maximum reliable window size
|
||||
* fixed RCVTIMEO/SNDTIMEO socket options and also added NODELAY
|
||||
|
||||
ENet 1.3.9 (August 19, 2013):
|
||||
|
||||
* added duplicatePeers option to ENetHost which can limit the number of peers from duplicate IPs
|
||||
* added enet_socket_get_option() and ENET_SOCKOPT_ERROR
|
||||
* added enet_host_random_seed() platform stub
|
||||
|
||||
ENet 1.3.8 (June 2, 2013):
|
||||
|
||||
* added enet_linked_version() for checking the linked version
|
||||
* added enet_socket_get_address() for querying the local address of a socket
|
||||
* silenced some debugging prints unless ENET_DEBUG is defined during compilation
|
||||
* handle EINTR in enet_socket_wait() so that enet_host_service() doesn't propagate errors from signals
|
||||
* optimized enet_host_bandwidth_throttle() to be less expensive for large numbers of peers
|
||||
|
||||
ENet 1.3.7 (March 6, 2013):
|
||||
|
||||
* added ENET_PACKET_FLAG_SENT to indicate that a packet is being freed because it has been sent
|
||||
* added userData field to ENetPacket
|
||||
* changed how random seed is generated on Windows to avoid import warnings
|
||||
* fixed case where disconnects could be generated with no preceding connect event
|
||||
|
||||
ENet 1.3.6 (December 11, 2012):
|
||||
|
||||
* added support for intercept callback in ENetHost that can be used to process raw packets before ENet
|
||||
* added enet_socket_shutdown() for issuing shutdown on a socket
|
||||
* fixed enet_socket_connect() to not error on non-blocking connects
|
||||
* fixed bug in MTU negotiation during connections
|
||||
|
||||
ENet 1.3.5 (July 31, 2012):
|
||||
|
||||
* fixed bug in unreliable packet fragment queuing
|
||||
|
||||
ENet 1.3.4 (May 29, 2012):
|
||||
|
||||
* added enet_peer_ping_interval() for configuring per-peer ping intervals
|
||||
* added enet_peer_timeout() for configuring per-peer timeouts
|
||||
* added protocol packet size limits
|
||||
|
||||
ENet 1.3.3 (June 28, 2011):
|
||||
|
||||
* fixed bug with simultaneous disconnects not dispatching events
|
||||
|
||||
ENet 1.3.2 (May 31, 2011):
|
||||
|
||||
* added support for unreliable packet fragmenting via the packet flag
|
||||
ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT
|
||||
* fixed regression in unreliable packet queuing
|
||||
* added check against received port to limit some forms of IP-spoofing
|
||||
|
||||
ENet 1.3.1 (February 10, 2011):
|
||||
|
||||
* fixed bug in tracking of reliable data in transit
|
||||
* reliable data window size now scales with the throttle
|
||||
* fixed bug in fragment length calculation when checksums are used
|
||||
|
||||
ENet 1.3.0 (June 5, 2010):
|
||||
|
||||
* enet_host_create() now requires the channel limit to be specified as
|
||||
a parameter
|
||||
* enet_host_connect() now accepts a data parameter which is supplied
|
||||
to the receiving receiving host in the event data field for a connect event
|
||||
* added an adaptive order-2 PPM range coder as a built-in compressor option
|
||||
which can be set with enet_host_compress_with_range_coder()
|
||||
* added support for packet compression configurable with a callback
|
||||
* improved session number handling to not rely on the packet checksum
|
||||
field, saving 4 bytes per packet unless the checksum option is used
|
||||
* removed the dependence on the rand callback for session number handling
|
||||
|
||||
Caveats: This version is not protocol compatible with the 1.2 series or
|
||||
earlier. The enet_host_connect and enet_host_create API functions require
|
||||
supplying additional parameters.
|
||||
|
||||
ENet 1.2.5 (June 28, 2011):
|
||||
|
||||
* fixed bug with simultaneous disconnects not dispatching events
|
||||
|
||||
ENet 1.2.4 (May 31, 2011):
|
||||
|
||||
* fixed regression in unreliable packet queuing
|
||||
* added check against received port to limit some forms of IP-spoofing
|
||||
|
||||
ENet 1.2.3 (February 10, 2011):
|
||||
|
||||
* fixed bug in tracking reliable data in transit
|
||||
|
||||
ENet 1.2.2 (June 5, 2010):
|
||||
|
||||
* checksum functionality is now enabled by setting a checksum callback
|
||||
inside ENetHost instead of being a configure script option
|
||||
* added totalSentData, totalSentPackets, totalReceivedData, and
|
||||
totalReceivedPackets counters inside ENetHost for getting usage
|
||||
statistics
|
||||
* added enet_host_channel_limit() for limiting the maximum number of
|
||||
channels allowed by connected peers
|
||||
* now uses dispatch queues for event dispatch rather than potentially
|
||||
unscalable array walking
|
||||
* added no_memory callback that is called when a malloc attempt fails,
|
||||
such that if no_memory returns rather than aborts (the default behavior),
|
||||
then the error is propagated to the return value of the API calls
|
||||
* now uses packed attribute for protocol structures on platforms with
|
||||
strange alignment rules
|
||||
* improved autoconf build system contributed by Nathan Brink allowing
|
||||
for easier building as a shared library
|
||||
|
||||
Caveats: If you were using the compile-time option that enabled checksums,
|
||||
make sure to set the checksum callback inside ENetHost to enet_crc32 to
|
||||
regain the old behavior. The ENetCallbacks structure has added new fields,
|
||||
so make sure to clear the structure to zero before use if
|
||||
using enet_initialize_with_callbacks().
|
||||
|
||||
ENet 1.2.1 (November 12, 2009):
|
||||
|
||||
* fixed bug that could cause disconnect events to be dropped
|
||||
* added thin wrapper around select() for portable usage
|
||||
* added ENET_SOCKOPT_REUSEADDR socket option
|
||||
* factored enet_socket_bind()/enet_socket_listen() out of enet_socket_create()
|
||||
* added contributed Code::Blocks build file
|
||||
|
||||
ENet 1.2 (February 12, 2008):
|
||||
|
||||
* fixed bug in VERIFY_CONNECT acknowledgement that could cause connect
|
||||
attempts to occasionally timeout
|
||||
* fixed acknowledgements to check both the outgoing and sent queues
|
||||
when removing acknowledged packets
|
||||
* fixed accidental bit rot in the MSVC project file
|
||||
* revised sequence number overflow handling to address some possible
|
||||
disconnect bugs
|
||||
* added enet_host_check_events() for getting only local queued events
|
||||
* factored out socket option setting into enet_socket_set_option() so
|
||||
that socket options are now set separately from enet_socket_create()
|
||||
|
||||
Caveats: While this release is superficially protocol compatible with 1.1,
|
||||
differences in the sequence number overflow handling can potentially cause
|
||||
random disconnects.
|
||||
|
||||
ENet 1.1 (June 6, 2007):
|
||||
|
||||
* optional CRC32 just in case someone needs a stronger checksum than UDP
|
||||
provides (--enable-crc32 configure option)
|
||||
* the size of packet headers are half the size they used to be (so less
|
||||
overhead when sending small packets)
|
||||
* enet_peer_disconnect_later() that waits till all queued outgoing
|
||||
packets get sent before issuing an actual disconnect
|
||||
* freeCallback field in individual packets for notification of when a
|
||||
packet is about to be freed
|
||||
* ENET_PACKET_FLAG_NO_ALLOCATE for supplying pre-allocated data to a
|
||||
packet (can be used in concert with freeCallback to support some custom
|
||||
allocation schemes that the normal memory allocation callbacks would
|
||||
normally not allow)
|
||||
* enet_address_get_host_ip() for printing address numbers
|
||||
* promoted the enet_socket_*() functions to be part of the API now
|
||||
* a few stability/crash fixes
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,191 @@
|
|||
<doxygenlayout version="1.0">
|
||||
<!-- Generated by doxygen 1.8.6 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title="Home"/>
|
||||
<tab type="user" visible="yes" title="Features" url="@ref Features" />
|
||||
<tab type="user" visible="yes" title="Downloads" url="@ref Downloads" />
|
||||
<tab type="user" visible="yes" title="Installation" url="@ref Installation" />
|
||||
<tab type="user" visible="yes" title="Tutorial" url="@ref Tutorial" />
|
||||
<tab type="user" visible="yes" title="Mailing List" url="@ref MailingList" />
|
||||
<tab type="user" visible="yes" title="IRC Channel" url="@ref IRCChannel" />
|
||||
<tab type="user" visible="yes" title="FAQ" url="@ref FAQ" />
|
||||
<tab type="user" visible="yes" title="License" url="@ref License" />
|
||||
<tab type="usergroup" visible="yes" title="Documentation" briefdescription="Documentation">
|
||||
<tab type="modules" visible="yes" title="Functions" intro=""/>
|
||||
<tab type="classlist" visible="yes" title="Data Structures" intro=""/>
|
||||
<tab type="filelist" visible="yes" title="Files" intro=""/>
|
||||
<tab type="globals" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<publicslots title=""/>
|
||||
<signals title=""/>
|
||||
<publicmethods title=""/>
|
||||
<publicstaticmethods title=""/>
|
||||
<publicattributes title=""/>
|
||||
<publicstaticattributes title=""/>
|
||||
<protectedtypes title=""/>
|
||||
<protectedslots title=""/>
|
||||
<protectedmethods title=""/>
|
||||
<protectedstaticmethods title=""/>
|
||||
<protectedattributes title=""/>
|
||||
<protectedstaticattributes title=""/>
|
||||
<packagetypes title=""/>
|
||||
<packagemethods title=""/>
|
||||
<packagestaticmethods title=""/>
|
||||
<packageattributes title=""/>
|
||||
<packagestaticattributes title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
<privatetypes title=""/>
|
||||
<privateslots title=""/>
|
||||
<privatemethods title=""/>
|
||||
<privatestaticmethods title=""/>
|
||||
<privateattributes title=""/>
|
||||
<privatestaticattributes title=""/>
|
||||
<friends title=""/>
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<constructors title=""/>
|
||||
<functions title=""/>
|
||||
<related title=""/>
|
||||
<variables title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
</memberdef>
|
||||
<allmemberslink visible="yes"/>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="$INCLUDE_GRAPH"/>
|
||||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<classes visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="$GROUP_GRAPHS"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
Copyright (c) 2002-2024 Lee Salzman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
nodist_pkgconfig_DATA = libenet.pc
|
||||
|
||||
enetincludedir=$(includedir)/enet
|
||||
enetinclude_HEADERS = \
|
||||
include/enet/callbacks.h \
|
||||
include/enet/enet.h \
|
||||
include/enet/list.h \
|
||||
include/enet/protocol.h \
|
||||
include/enet/time.h \
|
||||
include/enet/types.h \
|
||||
include/enet/unix.h \
|
||||
include/enet/utility.h \
|
||||
include/enet/win32.h
|
||||
|
||||
lib_LTLIBRARIES = libenet.la
|
||||
libenet_la_SOURCES = callbacks.c compress.c host.c list.c packet.c peer.c protocol.c unix.c win32.c
|
||||
# see info '(libtool) Updating version info' before making a release
|
||||
libenet_la_LDFLAGS = $(AM_LDFLAGS) -version-info 7:6:0
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
ACLOCAL_AMFLAGS = -Im4
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Please visit the ENet homepage at http://sauerbraten.org/enet/ for installation
|
||||
and usage instructions.
|
||||
|
||||
If you obtained this package from github, the quick description on how to build
|
||||
is:
|
||||
|
||||
# Generate the build system.
|
||||
|
||||
autoreconf -vfi
|
||||
|
||||
# Compile and install the library.
|
||||
|
||||
./configure && make && make install
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
@file callbacks.c
|
||||
@brief ENet callback functions
|
||||
*/
|
||||
#define ENET_BUILDING_LIB 1
|
||||
#include "enet/enet.h"
|
||||
|
||||
static ENetCallbacks callbacks = { malloc, free, abort };
|
||||
|
||||
int
|
||||
enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits)
|
||||
{
|
||||
if (version < ENET_VERSION_CREATE (1, 3, 0))
|
||||
return -1;
|
||||
|
||||
if (inits -> malloc != NULL || inits -> free != NULL)
|
||||
{
|
||||
if (inits -> malloc == NULL || inits -> free == NULL)
|
||||
return -1;
|
||||
|
||||
callbacks.malloc = inits -> malloc;
|
||||
callbacks.free = inits -> free;
|
||||
}
|
||||
|
||||
if (inits -> no_memory != NULL)
|
||||
callbacks.no_memory = inits -> no_memory;
|
||||
|
||||
return enet_initialize ();
|
||||
}
|
||||
|
||||
ENetVersion
|
||||
enet_linked_version (void)
|
||||
{
|
||||
return ENET_VERSION;
|
||||
}
|
||||
|
||||
void *
|
||||
enet_malloc (size_t size)
|
||||
{
|
||||
void * memory = callbacks.malloc (size);
|
||||
|
||||
if (memory == NULL)
|
||||
callbacks.no_memory ();
|
||||
|
||||
return memory;
|
||||
}
|
||||
|
||||
void
|
||||
enet_free (void * memory)
|
||||
{
|
||||
callbacks.free (memory);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,654 @@
|
|||
/**
|
||||
@file compress.c
|
||||
@brief An adaptive order-2 PPM range coder
|
||||
*/
|
||||
#define ENET_BUILDING_LIB 1
|
||||
#include <string.h>
|
||||
#include "enet/enet.h"
|
||||
|
||||
typedef struct _ENetSymbol
|
||||
{
|
||||
/* binary indexed tree of symbols */
|
||||
enet_uint8 value;
|
||||
enet_uint8 count;
|
||||
enet_uint16 under;
|
||||
enet_uint16 left, right;
|
||||
|
||||
/* context defined by this symbol */
|
||||
enet_uint16 symbols;
|
||||
enet_uint16 escapes;
|
||||
enet_uint16 total;
|
||||
enet_uint16 parent;
|
||||
} ENetSymbol;
|
||||
|
||||
/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */
|
||||
enum
|
||||
{
|
||||
ENET_RANGE_CODER_TOP = 1<<24,
|
||||
ENET_RANGE_CODER_BOTTOM = 1<<16,
|
||||
|
||||
ENET_CONTEXT_SYMBOL_DELTA = 3,
|
||||
ENET_CONTEXT_SYMBOL_MINIMUM = 1,
|
||||
ENET_CONTEXT_ESCAPE_MINIMUM = 1,
|
||||
|
||||
ENET_SUBCONTEXT_ORDER = 2,
|
||||
ENET_SUBCONTEXT_SYMBOL_DELTA = 2,
|
||||
ENET_SUBCONTEXT_ESCAPE_DELTA = 5
|
||||
};
|
||||
|
||||
/* context exclusion roughly halves compression speed, so disable for now */
|
||||
#undef ENET_CONTEXT_EXCLUSION
|
||||
|
||||
typedef struct _ENetRangeCoder
|
||||
{
|
||||
/* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */
|
||||
ENetSymbol symbols[4096];
|
||||
} ENetRangeCoder;
|
||||
|
||||
void *
|
||||
enet_range_coder_create (void)
|
||||
{
|
||||
ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder));
|
||||
if (rangeCoder == NULL)
|
||||
return NULL;
|
||||
|
||||
return rangeCoder;
|
||||
}
|
||||
|
||||
void
|
||||
enet_range_coder_destroy (void * context)
|
||||
{
|
||||
ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
|
||||
if (rangeCoder == NULL)
|
||||
return;
|
||||
|
||||
enet_free (rangeCoder);
|
||||
}
|
||||
|
||||
#define ENET_SYMBOL_CREATE(symbol, value_, count_) \
|
||||
{ \
|
||||
symbol = & rangeCoder -> symbols [nextSymbol ++]; \
|
||||
symbol -> value = value_; \
|
||||
symbol -> count = count_; \
|
||||
symbol -> under = count_; \
|
||||
symbol -> left = 0; \
|
||||
symbol -> right = 0; \
|
||||
symbol -> symbols = 0; \
|
||||
symbol -> escapes = 0; \
|
||||
symbol -> total = 0; \
|
||||
symbol -> parent = 0; \
|
||||
}
|
||||
|
||||
#define ENET_CONTEXT_CREATE(context, escapes_, minimum) \
|
||||
{ \
|
||||
ENET_SYMBOL_CREATE (context, 0, 0); \
|
||||
(context) -> escapes = escapes_; \
|
||||
(context) -> total = escapes_ + 256*minimum; \
|
||||
(context) -> symbols = 0; \
|
||||
}
|
||||
|
||||
static enet_uint16
|
||||
enet_symbol_rescale (ENetSymbol * symbol)
|
||||
{
|
||||
enet_uint16 total = 0;
|
||||
for (;;)
|
||||
{
|
||||
symbol -> count -= symbol->count >> 1;
|
||||
symbol -> under = symbol -> count;
|
||||
if (symbol -> left)
|
||||
symbol -> under += enet_symbol_rescale (symbol + symbol -> left);
|
||||
total += symbol -> under;
|
||||
if (! symbol -> right) break;
|
||||
symbol += symbol -> right;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
#define ENET_CONTEXT_RESCALE(context, minimum) \
|
||||
{ \
|
||||
(context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \
|
||||
(context) -> escapes -= (context) -> escapes >> 1; \
|
||||
(context) -> total += (context) -> escapes + 256*minimum; \
|
||||
}
|
||||
|
||||
#define ENET_RANGE_CODER_OUTPUT(value) \
|
||||
{ \
|
||||
if (outData >= outEnd) \
|
||||
return 0; \
|
||||
* outData ++ = value; \
|
||||
}
|
||||
|
||||
#define ENET_RANGE_CODER_ENCODE(under, count, total) \
|
||||
{ \
|
||||
encodeRange /= (total); \
|
||||
encodeLow += (under) * encodeRange; \
|
||||
encodeRange *= (count); \
|
||||
for (;;) \
|
||||
{ \
|
||||
if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \
|
||||
{ \
|
||||
if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
|
||||
encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
|
||||
} \
|
||||
ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
|
||||
encodeRange <<= 8; \
|
||||
encodeLow <<= 8; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ENET_RANGE_CODER_FLUSH \
|
||||
{ \
|
||||
while (encodeLow) \
|
||||
{ \
|
||||
ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
|
||||
encodeLow <<= 8; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ENET_RANGE_CODER_FREE_SYMBOLS \
|
||||
{ \
|
||||
if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \
|
||||
{ \
|
||||
nextSymbol = 0; \
|
||||
ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \
|
||||
predicted = 0; \
|
||||
order = 0; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \
|
||||
{ \
|
||||
under_ = value*minimum; \
|
||||
count_ = minimum; \
|
||||
if (! (context) -> symbols) \
|
||||
{ \
|
||||
ENET_SYMBOL_CREATE (symbol_, value_, update); \
|
||||
(context) -> symbols = symbol_ - (context); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
ENetSymbol * node = (context) + (context) -> symbols; \
|
||||
for (;;) \
|
||||
{ \
|
||||
if (value_ < node -> value) \
|
||||
{ \
|
||||
node -> under += update; \
|
||||
if (node -> left) { node += node -> left; continue; } \
|
||||
ENET_SYMBOL_CREATE (symbol_, value_, update); \
|
||||
node -> left = symbol_ - node; \
|
||||
} \
|
||||
else \
|
||||
if (value_ > node -> value) \
|
||||
{ \
|
||||
under_ += node -> under; \
|
||||
if (node -> right) { node += node -> right; continue; } \
|
||||
ENET_SYMBOL_CREATE (symbol_, value_, update); \
|
||||
node -> right = symbol_ - node; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
count_ += node -> count; \
|
||||
under_ += node -> under - node -> count; \
|
||||
node -> under += update; \
|
||||
node -> count += update; \
|
||||
symbol_ = node; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
#define ENET_CONTEXT_WALK(context, body) \
|
||||
{ \
|
||||
const ENetSymbol * node = (context) + (context) -> symbols; \
|
||||
const ENetSymbol * stack [256]; \
|
||||
size_t stackSize = 0; \
|
||||
while (node -> left) \
|
||||
{ \
|
||||
stack [stackSize ++] = node; \
|
||||
node += node -> left; \
|
||||
} \
|
||||
for (;;) \
|
||||
{ \
|
||||
body; \
|
||||
if (node -> right) \
|
||||
{ \
|
||||
node += node -> right; \
|
||||
while (node -> left) \
|
||||
{ \
|
||||
stack [stackSize ++] = node; \
|
||||
node += node -> left; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
if (stackSize <= 0) \
|
||||
break; \
|
||||
else \
|
||||
node = stack [-- stackSize]; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \
|
||||
ENET_CONTEXT_WALK(context, { \
|
||||
if (node -> value != value_) \
|
||||
{ \
|
||||
enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \
|
||||
if (node -> value < value_) \
|
||||
under -= parentCount; \
|
||||
total -= parentCount; \
|
||||
} \
|
||||
})
|
||||
#endif
|
||||
|
||||
size_t
|
||||
enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit)
|
||||
{
|
||||
ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
|
||||
enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
|
||||
const enet_uint8 * inData, * inEnd;
|
||||
enet_uint32 encodeLow = 0, encodeRange = ~0;
|
||||
ENetSymbol * root;
|
||||
enet_uint16 predicted = 0;
|
||||
size_t order = 0, nextSymbol = 0;
|
||||
|
||||
if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0)
|
||||
return 0;
|
||||
|
||||
inData = (const enet_uint8 *) inBuffers -> data;
|
||||
inEnd = & inData [inBuffers -> dataLength];
|
||||
inBuffers ++;
|
||||
inBufferCount --;
|
||||
|
||||
ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ENetSymbol * subcontext, * symbol;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
const ENetSymbol * childContext = & emptyContext;
|
||||
#endif
|
||||
enet_uint8 value;
|
||||
enet_uint16 count, under, * parent = & predicted, total;
|
||||
if (inData >= inEnd)
|
||||
{
|
||||
if (inBufferCount <= 0)
|
||||
break;
|
||||
inData = (const enet_uint8 *) inBuffers -> data;
|
||||
inEnd = & inData [inBuffers -> dataLength];
|
||||
inBuffers ++;
|
||||
inBufferCount --;
|
||||
}
|
||||
value = * inData ++;
|
||||
|
||||
for (subcontext = & rangeCoder -> symbols [predicted];
|
||||
subcontext != root;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
childContext = subcontext,
|
||||
#endif
|
||||
subcontext = & rangeCoder -> symbols [subcontext -> parent])
|
||||
{
|
||||
ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
|
||||
* parent = symbol - rangeCoder -> symbols;
|
||||
parent = & symbol -> parent;
|
||||
total = subcontext -> total;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
|
||||
ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0);
|
||||
#endif
|
||||
if (count > 0)
|
||||
{
|
||||
ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (subcontext -> escapes > 0 && subcontext -> escapes < total)
|
||||
ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total);
|
||||
subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
|
||||
subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
|
||||
}
|
||||
subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
|
||||
if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
|
||||
ENET_CONTEXT_RESCALE (subcontext, 0);
|
||||
if (count > 0) goto nextInput;
|
||||
}
|
||||
|
||||
ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM);
|
||||
* parent = symbol - rangeCoder -> symbols;
|
||||
parent = & symbol -> parent;
|
||||
total = root -> total;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
|
||||
ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM);
|
||||
#endif
|
||||
ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total);
|
||||
root -> total += ENET_CONTEXT_SYMBOL_DELTA;
|
||||
if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
|
||||
ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
|
||||
|
||||
nextInput:
|
||||
if (order >= ENET_SUBCONTEXT_ORDER)
|
||||
predicted = rangeCoder -> symbols [predicted].parent;
|
||||
else
|
||||
order ++;
|
||||
ENET_RANGE_CODER_FREE_SYMBOLS;
|
||||
}
|
||||
|
||||
ENET_RANGE_CODER_FLUSH;
|
||||
|
||||
return (size_t) (outData - outStart);
|
||||
}
|
||||
|
||||
#define ENET_RANGE_CODER_SEED \
|
||||
{ \
|
||||
if (inData < inEnd) decodeCode |= * inData ++ << 24; \
|
||||
if (inData < inEnd) decodeCode |= * inData ++ << 16; \
|
||||
if (inData < inEnd) decodeCode |= * inData ++ << 8; \
|
||||
if (inData < inEnd) decodeCode |= * inData ++; \
|
||||
}
|
||||
|
||||
#define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total)))
|
||||
|
||||
#define ENET_RANGE_CODER_DECODE(under, count, total) \
|
||||
{ \
|
||||
decodeLow += (under) * decodeRange; \
|
||||
decodeRange *= (count); \
|
||||
for (;;) \
|
||||
{ \
|
||||
if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \
|
||||
{ \
|
||||
if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
|
||||
decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
|
||||
} \
|
||||
decodeCode <<= 8; \
|
||||
if (inData < inEnd) \
|
||||
decodeCode |= * inData ++; \
|
||||
decodeRange <<= 8; \
|
||||
decodeLow <<= 8; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \
|
||||
{ \
|
||||
under_ = 0; \
|
||||
count_ = minimum; \
|
||||
if (! (context) -> symbols) \
|
||||
{ \
|
||||
createRoot; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
ENetSymbol * node = (context) + (context) -> symbols; \
|
||||
for (;;) \
|
||||
{ \
|
||||
enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \
|
||||
visitNode; \
|
||||
if (code >= after) \
|
||||
{ \
|
||||
under_ += node -> under; \
|
||||
if (node -> right) { node += node -> right; continue; } \
|
||||
createRight; \
|
||||
} \
|
||||
else \
|
||||
if (code < after - before) \
|
||||
{ \
|
||||
node -> under += update; \
|
||||
if (node -> left) { node += node -> left; continue; } \
|
||||
createLeft; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
value_ = node -> value; \
|
||||
count_ += node -> count; \
|
||||
under_ = after - before; \
|
||||
node -> under += update; \
|
||||
node -> count += update; \
|
||||
symbol_ = node; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
|
||||
ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0)
|
||||
|
||||
#define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
|
||||
ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \
|
||||
{ \
|
||||
value_ = code / minimum; \
|
||||
under_ = code - code%minimum; \
|
||||
ENET_SYMBOL_CREATE (symbol_, value_, update); \
|
||||
(context) -> symbols = symbol_ - (context); \
|
||||
}, \
|
||||
exclude (node -> value, after, before), \
|
||||
{ \
|
||||
value_ = node->value + 1 + (code - after)/minimum; \
|
||||
under_ = code - (code - after)%minimum; \
|
||||
ENET_SYMBOL_CREATE (symbol_, value_, update); \
|
||||
node -> right = symbol_ - node; \
|
||||
}, \
|
||||
{ \
|
||||
value_ = node->value - 1 - (after - before - code - 1)/minimum; \
|
||||
under_ = code - (after - before - code - 1)%minimum; \
|
||||
ENET_SYMBOL_CREATE (symbol_, value_, update); \
|
||||
node -> left = symbol_ - node; \
|
||||
}) \
|
||||
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
typedef struct _ENetExclude
|
||||
{
|
||||
enet_uint8 value;
|
||||
enet_uint16 under;
|
||||
} ENetExclude;
|
||||
|
||||
#define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \
|
||||
{ \
|
||||
enet_uint16 under = 0; \
|
||||
nextExclude = excludes; \
|
||||
ENET_CONTEXT_WALK (context, { \
|
||||
under += rangeCoder -> symbols [node -> parent].count + minimum; \
|
||||
nextExclude -> value = node -> value; \
|
||||
nextExclude -> under = under; \
|
||||
nextExclude ++; \
|
||||
}); \
|
||||
total -= under; \
|
||||
}
|
||||
|
||||
#define ENET_CONTEXT_EXCLUDED(value_, after, before) \
|
||||
{ \
|
||||
size_t low = 0, high = nextExclude - excludes; \
|
||||
for(;;) \
|
||||
{ \
|
||||
size_t mid = (low + high) >> 1; \
|
||||
const ENetExclude * exclude = & excludes [mid]; \
|
||||
if (value_ < exclude -> value) \
|
||||
{ \
|
||||
if (low + 1 < high) \
|
||||
{ \
|
||||
high = mid; \
|
||||
continue; \
|
||||
} \
|
||||
if (exclude > excludes) \
|
||||
after -= exclude [-1].under; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (value_ > exclude -> value) \
|
||||
{ \
|
||||
if (low + 1 < high) \
|
||||
{ \
|
||||
low = mid; \
|
||||
continue; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
before = 0; \
|
||||
after -= exclude -> under; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before)
|
||||
|
||||
size_t
|
||||
enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit)
|
||||
{
|
||||
ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
|
||||
enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
|
||||
const enet_uint8 * inEnd = & inData [inLimit];
|
||||
enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0;
|
||||
ENetSymbol * root;
|
||||
enet_uint16 predicted = 0;
|
||||
size_t order = 0, nextSymbol = 0;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
ENetExclude excludes [256];
|
||||
ENetExclude * nextExclude = excludes;
|
||||
#endif
|
||||
|
||||
if (rangeCoder == NULL || inLimit <= 0)
|
||||
return 0;
|
||||
|
||||
ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
|
||||
|
||||
ENET_RANGE_CODER_SEED;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ENetSymbol * subcontext, * symbol, * patch;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
const ENetSymbol * childContext = & emptyContext;
|
||||
#endif
|
||||
enet_uint8 value = 0;
|
||||
enet_uint16 code, under, count, bottom, * parent = & predicted, total;
|
||||
|
||||
for (subcontext = & rangeCoder -> symbols [predicted];
|
||||
subcontext != root;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
childContext = subcontext,
|
||||
#endif
|
||||
subcontext = & rangeCoder -> symbols [subcontext -> parent])
|
||||
{
|
||||
if (subcontext -> escapes <= 0)
|
||||
continue;
|
||||
total = subcontext -> total;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
if (childContext -> total > 0)
|
||||
ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0);
|
||||
#endif
|
||||
if (subcontext -> escapes >= total)
|
||||
continue;
|
||||
code = ENET_RANGE_CODER_READ (total);
|
||||
if (code < subcontext -> escapes)
|
||||
{
|
||||
ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total);
|
||||
continue;
|
||||
}
|
||||
code -= subcontext -> escapes;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
if (childContext -> total > 0)
|
||||
{
|
||||
ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED);
|
||||
}
|
||||
bottom = symbol - rangeCoder -> symbols;
|
||||
ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total);
|
||||
subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
|
||||
if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
|
||||
ENET_CONTEXT_RESCALE (subcontext, 0);
|
||||
goto patchContexts;
|
||||
}
|
||||
|
||||
total = root -> total;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
if (childContext -> total > 0)
|
||||
ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM);
|
||||
#endif
|
||||
code = ENET_RANGE_CODER_READ (total);
|
||||
if (code < root -> escapes)
|
||||
{
|
||||
ENET_RANGE_CODER_DECODE (0, root -> escapes, total);
|
||||
break;
|
||||
}
|
||||
code -= root -> escapes;
|
||||
#ifdef ENET_CONTEXT_EXCLUSION
|
||||
if (childContext -> total > 0)
|
||||
{
|
||||
ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED);
|
||||
}
|
||||
bottom = symbol - rangeCoder -> symbols;
|
||||
ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total);
|
||||
root -> total += ENET_CONTEXT_SYMBOL_DELTA;
|
||||
if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
|
||||
ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
|
||||
|
||||
patchContexts:
|
||||
for (patch = & rangeCoder -> symbols [predicted];
|
||||
patch != subcontext;
|
||||
patch = & rangeCoder -> symbols [patch -> parent])
|
||||
{
|
||||
ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
|
||||
* parent = symbol - rangeCoder -> symbols;
|
||||
parent = & symbol -> parent;
|
||||
if (count <= 0)
|
||||
{
|
||||
patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
|
||||
patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
|
||||
}
|
||||
patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
|
||||
if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
|
||||
ENET_CONTEXT_RESCALE (patch, 0);
|
||||
}
|
||||
* parent = bottom;
|
||||
|
||||
ENET_RANGE_CODER_OUTPUT (value);
|
||||
|
||||
if (order >= ENET_SUBCONTEXT_ORDER)
|
||||
predicted = rangeCoder -> symbols [predicted].parent;
|
||||
else
|
||||
order ++;
|
||||
ENET_RANGE_CODER_FREE_SYMBOLS;
|
||||
}
|
||||
|
||||
return (size_t) (outData - outStart);
|
||||
}
|
||||
|
||||
/** @defgroup host ENet host functions
|
||||
@{
|
||||
*/
|
||||
|
||||
/** Sets the packet compressor the host should use to the default range coder.
|
||||
@param host host to enable the range coder for
|
||||
@returns 0 on success, < 0 on failure
|
||||
*/
|
||||
int
|
||||
enet_host_compress_with_range_coder (ENetHost * host)
|
||||
{
|
||||
ENetCompressor compressor;
|
||||
memset (& compressor, 0, sizeof (compressor));
|
||||
compressor.context = enet_range_coder_create();
|
||||
if (compressor.context == NULL)
|
||||
return -1;
|
||||
compressor.compress = enet_range_coder_compress;
|
||||
compressor.decompress = enet_range_coder_decompress;
|
||||
compressor.destroy = enet_range_coder_destroy;
|
||||
enet_host_compress (host, & compressor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
AC_INIT([libenet], [1.3.18])
|
||||
AC_CONFIG_SRCDIR([include/enet/enet.h])
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
AC_CHECK_FUNC(getaddrinfo, [AC_DEFINE(HAS_GETADDRINFO)])
|
||||
AC_CHECK_FUNC(getnameinfo, [AC_DEFINE(HAS_GETNAMEINFO)])
|
||||
AC_CHECK_FUNC(gethostbyaddr_r, [AC_DEFINE(HAS_GETHOSTBYADDR_R)])
|
||||
AC_CHECK_FUNC(gethostbyname_r, [AC_DEFINE(HAS_GETHOSTBYNAME_R)])
|
||||
AC_CHECK_FUNC(poll, [AC_DEFINE(HAS_POLL)])
|
||||
AC_CHECK_FUNC(fcntl, [AC_DEFINE(HAS_FCNTL)])
|
||||
AC_CHECK_FUNC(inet_pton, [AC_DEFINE(HAS_INET_PTON)])
|
||||
AC_CHECK_FUNC(inet_ntop, [AC_DEFINE(HAS_INET_NTOP)])
|
||||
AC_CHECK_DECLS(offsetof, [AC_DEFINE(HAS_OFFSETOF)], [], [#include <stddef.h>])
|
||||
|
||||
AC_CHECK_MEMBER(struct msghdr.msg_flags, [AC_DEFINE(HAS_MSGHDR_FLAGS)], , [#include <sys/socket.h>])
|
||||
|
||||
AC_CHECK_TYPE(socklen_t, [AC_DEFINE(HAS_SOCKLEN_T)], ,
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
)
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
libenet.pc])
|
||||
AC_OUTPUT
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
@page FAQ Frequently Answered Questions
|
||||
|
||||
@section Q1 Is ENet thread-safe?
|
||||
|
||||
ENet does not use any significant global variables, the vast majority
|
||||
of state is encapsulated in the ENetHost structure. As such, as long
|
||||
as the application guards access to this structure, then ENet should
|
||||
operate fine in a multi-threaded environment.
|
||||
|
||||
@section Q2 Isn't ENet just re-inventing TCP?! What's the point?
|
||||
|
||||
In a perfect world, that would be true. But as many have found, using
|
||||
TCP either in lieu of or in conjunction with UDP can lead to all kinds
|
||||
of nightmares. TCP is a good, solid protocol, however it simply isn't
|
||||
up to the task of real-time games. Too much of TCP's implementation
|
||||
dictates a policy that isn't practical for games. If you want to use
|
||||
TCP, then do so -- this library is for people that either don't want
|
||||
to use TCP or have tried and ended up being discouraged with the
|
||||
performance.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
@page Features Features and Architecture
|
||||
|
||||
ENet evolved specifically as a UDP networking layer for the
|
||||
multiplayer first person shooter Cube. Cube necessitated low latency
|
||||
communication with data sent out very frequently, so TCP was an
|
||||
unsuitable choice due to its high latency and stream orientation. UDP,
|
||||
however, lacks many sometimes necessary features from TCP such as
|
||||
reliability, sequencing, unrestricted packet sizes, and connection
|
||||
management. So UDP by itself was not suitable as a network protocol
|
||||
either. No suitable freely available networking libraries existed at
|
||||
the time of ENet's creation to fill this niche.
|
||||
|
||||
UDP and TCP could have been used together in Cube to benefit somewhat
|
||||
from both of their features, however, the resulting combinations of
|
||||
protocols still leaves much to be desired. TCP lacks multiple streams
|
||||
of communication without resorting to opening many sockets and
|
||||
complicates delineation of packets due to its buffering behavior. UDP
|
||||
lacks sequencing, connection management, management of bandwidth
|
||||
resources, and imposes limitations on the size of packets. A
|
||||
significant investment is required to integrate these two protocols,
|
||||
and the end result is worse off in features and performance than the
|
||||
uniform protocol presented by ENet.
|
||||
|
||||
ENet thus attempts to address these issues and provide a single,
|
||||
uniform protocol layered over UDP to the developer with the best
|
||||
features of UDP and TCP as well as some useful features neither
|
||||
provide, with a much cleaner integration than any resulting from a
|
||||
mixture of UDP and TCP.
|
||||
|
||||
@section CM Connection Management
|
||||
|
||||
ENet provides a simple connection interface over which to communicate
|
||||
with a foreign host. The liveness of the connection is actively
|
||||
monitored by pinging the foreign host at frequent intervals, and also
|
||||
monitors the network conditions from the local host to the foreign
|
||||
host such as the mean round trip time and packet loss in this fashion.
|
||||
|
||||
@section Sequencing Sequencing
|
||||
|
||||
Rather than a single byte stream that complicates the delineation of
|
||||
packets, ENet presents connections as multiple, properly sequenced
|
||||
packet streams that simplify the transfer of various types of data.
|
||||
|
||||
ENet provides sequencing for all packets by assigning to each sent
|
||||
packet a sequence number that is incremented as packets are sent. ENet
|
||||
guarantees that no packet with a higher sequence number will be
|
||||
delivered before a packet with a lower sequence number, thus ensuring
|
||||
packets are delivered exactly in the order they are sent.
|
||||
|
||||
For unreliable packets, ENet will simply discard the lower sequence
|
||||
number packet if a packet with a higher sequence number has already
|
||||
been delivered. This allows the packets to be dispatched immediately
|
||||
as they arrive, and reduce latency of unreliable packets to an
|
||||
absolute minimum. For reliable packets, if a higher sequence number
|
||||
packet arrives, but the preceding packets in the sequence have not yet
|
||||
arrived, ENet will stall delivery of the higher sequence number
|
||||
packets until its predecessors have arrived.
|
||||
|
||||
@section Channels Channels
|
||||
|
||||
Since ENet will stall delivery of reliable packets to ensure proper
|
||||
sequencing, and consequently any packets of higher sequence number
|
||||
whether reliable or unreliable, in the event the reliable packet's
|
||||
predecessors have not yet arrived, this can introduce latency into the
|
||||
delivery of other packets which may not need to be as strictly ordered
|
||||
with respect to the packet that stalled their delivery.
|
||||
|
||||
To combat this latency and reduce the ordering restrictions on
|
||||
packets, ENet provides multiple channels of communication over a given
|
||||
connection. Each channel is independently sequenced, and so the
|
||||
delivery status of a packet in one channel will not stall the delivery
|
||||
of other packets in another channel.
|
||||
|
||||
@section Reliability Reliability
|
||||
|
||||
ENet provides optional reliability of packet delivery by ensuring the
|
||||
foreign host acknowledges receipt of all reliable packets. ENet will
|
||||
attempt to resend the packet up to a reasonable amount of times, if no
|
||||
acknowledgement of the packet's receipt happens within a specified
|
||||
timeout. Retry timeouts are progressive and become more lenient with
|
||||
every failed attempt to allow for temporary turbulence in network
|
||||
conditions.
|
||||
|
||||
@section FaR Fragmentation and Reassembly
|
||||
|
||||
ENet will send and deliver packets regardless of size. Large packets
|
||||
are fragmented into many smaller packets of suitable size, and
|
||||
reassembled on the foreign host to recover the original packet for
|
||||
delivery. The process is entirely transparent to the developer.
|
||||
|
||||
@section Aggregation Aggregation
|
||||
|
||||
ENet aggregates all protocol commands, including acknowledgements and
|
||||
packet transfer, into larger protocol packets to ensure the proper
|
||||
utilization of the connection and to limit the opportunities for
|
||||
packet loss that might otherwise result in further delivery latency.
|
||||
|
||||
@section Adaptability Adaptability
|
||||
|
||||
ENet provides an in-flight data window for reliable packets to ensure
|
||||
connections are not overwhelmed by volumes of packets. It also
|
||||
provides a static bandwidth allocation mechanism to ensure the total
|
||||
volume of packets sent and received to a host don't exceed the host's
|
||||
capabilities. Further, ENet also provides a dynamic throttle that
|
||||
responds to deviations from normal network connections to rectify
|
||||
various types of network congestion by further limiting the volume of
|
||||
packets sent.
|
||||
|
||||
@section Portability Portability
|
||||
|
||||
ENet works on Windows and any other Unix or Unix-like platform
|
||||
providing a BSD sockets interface. The library has a small and stable
|
||||
code base that can easily be extended to support other platforms and
|
||||
integrates easily. ENet makes no assumptions about the underlying
|
||||
platform's endianess or word size.
|
||||
|
||||
@section Freedom Freedom
|
||||
|
||||
ENet demands no royalties and doesn't carry a viral license that would
|
||||
restrict you in how you might use it in your programs. ENet is
|
||||
licensed under a short-and-sweet MIT-style license, which gives you
|
||||
the freedom to do anything you want with it (well, almost anything).
|
||||
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
@page Installation Installation
|
||||
|
||||
ENet should be trivially simple to integrate with most applications.
|
||||
First, make sure you download the latest source distribution at @ref Downloads.
|
||||
|
||||
@section Unix Unix-like Operating Systems
|
||||
|
||||
If you are using an ENet release, then you should simply be able to build it
|
||||
by doing the following:
|
||||
|
||||
./configure && make && make install
|
||||
|
||||
If you obtained the package from github, you must have automake and autoconf
|
||||
available to generate the build system first by doing the following command
|
||||
before using the above mentioned build procedure:
|
||||
|
||||
autoreconf -vfi
|
||||
|
||||
|
||||
@subsection SolarisBSD Solaris and BSD
|
||||
|
||||
When building ENet under Solaris, you must specify the -lsocket and
|
||||
-lnsl parameters to your compiler to ensure that the sockets library
|
||||
is linked in.
|
||||
|
||||
@section Windows Microsoft Windows
|
||||
|
||||
You may simply use the included "enet.lib" or "enet64.lib" static libraries.
|
||||
However, if you wish to build the library yourself, then the following
|
||||
instructions apply:
|
||||
|
||||
There is an included MSVC 6 project (enet.dsp) which you may use to
|
||||
build a suitable library file. Alternatively, you may simply drag all
|
||||
the ENet source files into your main project.
|
||||
|
||||
You will have to link to the Winsock2 libraries, so make sure to add
|
||||
ws2_32.lib and winmm.lib to your library list (Project Settings | Link |
|
||||
Object/library modules).
|
||||
|
||||
@subsection enet.dsp Building with the included enet.dsp
|
||||
|
||||
Load the included enet.dsp. MSVC may ask you to convert it if you
|
||||
are on a newer version of MSVC - just allow the conversion and save
|
||||
the resulting project as "enet" or similar. After you build this
|
||||
project, it will output an "enet.lib" file to either the "Debug/"
|
||||
or "Release/" directory, depending on which configuration you have
|
||||
selected to build. By default, it should produce "Debug/enet.lib".
|
||||
|
||||
You may then copy the resulting "enet.lib" file and the header files
|
||||
found in the "include/" directory to your other projects and add it to
|
||||
their library lists. Make sure to also link against "ws2_32.lib" and
|
||||
"winmm.lib" as described above.
|
||||
|
||||
@subsection DLL DLL
|
||||
|
||||
If you wish to build ENet as a DLL you must first define ENET_DLL
|
||||
within the project (Project Settings | C/C++ | Preprocessor |
|
||||
Preprocessor definitions) or, more invasively, simply define ENET_DLL
|
||||
at the top of enet.h.
|
||||
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
@page License License
|
||||
|
||||
Copyright (c) 2002-2024 Lee Salzman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/** @mainpage ENet
|
||||
|
||||
ENet's purpose is to provide a relatively thin, simple and robust
|
||||
network communication layer on top of UDP (User Datagram Protocol).
|
||||
The primary feature it provides is optional reliable, in-order
|
||||
delivery of packets.
|
||||
|
||||
ENet omits certain higher level networking features such as authentication,
|
||||
lobbying, server discovery, encryption, or other similar tasks that are
|
||||
particularly application specific so that the library remains flexible,
|
||||
portable, and easily embeddable.
|
||||
|
||||
@ref Features
|
||||
|
||||
@ref Downloads
|
||||
|
||||
@ref Installation
|
||||
|
||||
@ref Tutorial
|
||||
|
||||
@ref MailingList
|
||||
|
||||
@ref IRCChannel
|
||||
|
||||
@ref FAQ
|
||||
|
||||
@ref License
|
||||
|
||||
<a class="el" href="usergroup0.html">Documentation</a>
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
@page Downloads Downloads
|
||||
|
||||
You can retrieve the source to ENet by downloading it in either .tar.gz form
|
||||
or accessing the github distribution directly.
|
||||
|
||||
The most recent stable release (1.3.18) can be downloaded <a class="el" href="download/enet-1.3.18.tar.gz">here</a>.
|
||||
The last release that is protocol compatible with the 1.2 series or earlier (1.2.5) can be downloaded <a class="el" href="download/enet-1.2.5.tar.gz">here</a>.
|
||||
|
||||
You can find the most recent ENet source at <a class="el" href="https://github.com/lsalzman/enet">the github repository</a>.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
@page MailingList Mailing List
|
||||
|
||||
The <a class="el" href="http://lists.cubik.org/mailman/listinfo/enet-discuss">enet-discuss</a> list is for discussion of ENet, including bug reports or feature requests.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
@page IRCChannel IRC Channel
|
||||
|
||||
Join the \#enet channel on the <a class="el" href="https://libera.chat">Libera Chat IRC network (irc.libera.chat)</a> for real-time discussion about the ENet library.
|
||||
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,366 @@
|
|||
/**
|
||||
@page Tutorial Tutorial
|
||||
|
||||
@ref Initialization
|
||||
|
||||
@ref CreateServer
|
||||
|
||||
@ref CreateClient
|
||||
|
||||
@ref ManageHost
|
||||
|
||||
@ref SendingPacket
|
||||
|
||||
@ref Disconnecting
|
||||
|
||||
@ref Connecting
|
||||
|
||||
@section Initialization Initialization
|
||||
|
||||
You should include the file <enet/enet.h> when using ENet. Do not
|
||||
include <enet.h> without the directory prefix, as this may cause
|
||||
file name conflicts on some systems.
|
||||
|
||||
Before using ENet, you must call enet_initialize() to initialize the
|
||||
library. Upon program exit, you should call enet_deinitialize() so
|
||||
that the library may clean up any used resources.
|
||||
|
||||
@code
|
||||
#include <enet/enet.h>
|
||||
|
||||
int
|
||||
main (int argc, char ** argv)
|
||||
{
|
||||
if (enet_initialize () != 0)
|
||||
{
|
||||
fprintf (stderr, "An error occurred while initializing ENet.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
atexit (enet_deinitialize);
|
||||
...
|
||||
...
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
|
||||
@section CreateServer Creating an ENet server
|
||||
|
||||
Servers in ENet are constructed with enet_host_create(). You must
|
||||
specify an address on which to receive data and new connections, as
|
||||
well as the maximum allowable numbers of connected peers. You may
|
||||
optionally specify the incoming and outgoing bandwidth of the server
|
||||
in bytes per second so that ENet may try to statically manage
|
||||
bandwidth resources among connected peers in addition to its dynamic
|
||||
throttling algorithm; specifying 0 for these two options will cause
|
||||
ENet to rely entirely upon its dynamic throttling algorithm to manage
|
||||
bandwidth.
|
||||
|
||||
When done with a host, the host may be destroyed with
|
||||
enet_host_destroy(). All connected peers to the host will be reset,
|
||||
and the resources used by the host will be freed.
|
||||
|
||||
@code
|
||||
ENetAddress address;
|
||||
ENetHost * server;
|
||||
|
||||
/* Bind the server to the default localhost. */
|
||||
/* A specific host address can be specified by */
|
||||
/* enet_address_set_host (& address, "x.x.x.x"); */
|
||||
|
||||
address.host = ENET_HOST_ANY;
|
||||
/* Bind the server to port 1234. */
|
||||
address.port = 1234;
|
||||
|
||||
server = enet_host_create (& address /* the address to bind the server host to */,
|
||||
32 /* allow up to 32 clients and/or outgoing connections */,
|
||||
2 /* allow up to 2 channels to be used, 0 and 1 */,
|
||||
0 /* assume any amount of incoming bandwidth */,
|
||||
0 /* assume any amount of outgoing bandwidth */);
|
||||
if (server == NULL)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"An error occurred while trying to create an ENet server host.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
...
|
||||
...
|
||||
...
|
||||
enet_host_destroy(server);
|
||||
@endcode
|
||||
|
||||
@section CreateClient Creating an ENet client
|
||||
|
||||
Clients in ENet are similarly constructed with enet_host_create() when
|
||||
no address is specified to bind the host to. Bandwidth may be
|
||||
specified for the client host as in the above example. The peer count
|
||||
controls the maximum number of connections to other server hosts that
|
||||
may be simultaneously open.
|
||||
|
||||
@code
|
||||
ENetHost * client;
|
||||
|
||||
client = enet_host_create (NULL /* create a client host */,
|
||||
1 /* only allow 1 outgoing connection */,
|
||||
2 /* allow up 2 channels to be used, 0 and 1 */,
|
||||
0 /* assume any amount of incoming bandwidth */,
|
||||
0 /* assume any amount of outgoing bandwidth */);
|
||||
|
||||
if (client == NULL)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"An error occurred while trying to create an ENet client host.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
...
|
||||
...
|
||||
...
|
||||
enet_host_destroy(client);
|
||||
@endcode
|
||||
|
||||
@section ManageHost Managing an ENet host
|
||||
|
||||
ENet uses a polled event model to notify the programmer of significant
|
||||
events. ENet hosts are polled for events with enet_host_service(),
|
||||
where an optional timeout value in milliseconds may be specified to
|
||||
control how long ENet will poll; if a timeout of 0 is specified,
|
||||
enet_host_service() will return immediately if there are no events to
|
||||
dispatch. enet_host_service() will return 1 if an event was dispatched
|
||||
within the specified timeout.
|
||||
|
||||
Beware that most processing of the network with the ENet stack is done
|
||||
inside enet_host_service(). Both hosts that make up the sides of a connection
|
||||
must regularly call this function to ensure packets are actually sent and
|
||||
received. A common symptom of not actively calling enet_host_service()
|
||||
on both ends is that one side receives events while the other does not.
|
||||
The best way to schedule this activity to ensure adequate service is, for
|
||||
example, to call enet_host_service() with a 0 timeout (meaning non-blocking)
|
||||
at the beginning of every frame in a game loop.
|
||||
|
||||
Currently there are only four types of significant events in ENet:
|
||||
|
||||
An event of type ENET_EVENT_TYPE_NONE is returned if no event occurred
|
||||
within the specified time limit. enet_host_service() will return 0
|
||||
with this event.
|
||||
|
||||
An event of type ENET_EVENT_TYPE_CONNECT is returned when either a new client
|
||||
host has connected to the server host or when an attempt to establish a
|
||||
connection with a foreign host has succeeded. Only the "peer" field of the
|
||||
event structure is valid for this event and contains the newly connected peer.
|
||||
|
||||
An event of type ENET_EVENT_TYPE_RECEIVE is returned when a packet is received
|
||||
from a connected peer. The "peer" field contains the peer the packet was
|
||||
received from, "channelID" is the channel on which the packet was sent, and
|
||||
"packet" is the packet that was sent. The packet contained in the "packet"
|
||||
field must be destroyed with enet_packet_destroy() when you are done
|
||||
inspecting its contents.
|
||||
|
||||
An event of type ENET_EVENT_TYPE_DISCONNECT is returned when a connected peer
|
||||
has either explicitly disconnected or timed out. Only the "peer" field of the
|
||||
event structure is valid for this event and contains the peer that
|
||||
disconnected. Only the "data" field of the peer is still valid on a
|
||||
disconnect event and must be explicitly reset.
|
||||
|
||||
@code
|
||||
ENetEvent event;
|
||||
|
||||
/* Wait up to 1000 milliseconds for an event. */
|
||||
while (enet_host_service (client, & event, 1000) > 0)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
printf ("A new client connected from %x:%u.\n",
|
||||
event.peer -> address.host,
|
||||
event.peer -> address.port);
|
||||
|
||||
/* Store any relevant client information here. */
|
||||
event.peer -> data = "Client information";
|
||||
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
printf ("A packet of length %u containing %s was received from %s on channel %u.\n",
|
||||
event.packet -> dataLength,
|
||||
event.packet -> data,
|
||||
event.peer -> data,
|
||||
event.channelID);
|
||||
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy (event.packet);
|
||||
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
printf ("%s disconnected.\n", event.peer -> data);
|
||||
|
||||
/* Reset the peer's client information. */
|
||||
|
||||
event.peer -> data = NULL;
|
||||
}
|
||||
}
|
||||
...
|
||||
...
|
||||
...
|
||||
@endcode
|
||||
|
||||
@section SendingPacket Sending a packet to an ENet peer
|
||||
|
||||
Packets in ENet are created with enet_packet_create(), where the size
|
||||
of the packet must be specified. Optionally, initial data may be
|
||||
specified to copy into the packet.
|
||||
|
||||
Certain flags may also be supplied to enet_packet_create() to control
|
||||
various packet features:
|
||||
|
||||
ENET_PACKET_FLAG_RELIABLE specifies that the packet must use reliable
|
||||
delivery. A reliable packet is guaranteed to be delivered, and a
|
||||
number of retry attempts will be made until an acknowledgement is
|
||||
received from the foreign host the packet is sent to. If a certain
|
||||
number of retry attempts is reached without any acknowledgement, ENet
|
||||
will assume the peer has disconnected and forcefully reset the
|
||||
connection. If this flag is not specified, the packet is assumed an
|
||||
unreliable packet, and no retry attempts will be made nor
|
||||
acknowledgements generated.
|
||||
|
||||
A packet may be resized (extended or truncated) with
|
||||
enet_packet_resize().
|
||||
|
||||
A packet is sent to a foreign host with
|
||||
enet_peer_send(). enet_peer_send() accepts a channel id over which to
|
||||
send the packet to a given peer. Once the packet is handed over to
|
||||
ENet with enet_peer_send(), ENet will handle its deallocation and
|
||||
enet_packet_destroy() should not be used upon it.
|
||||
|
||||
One may also use enet_host_broadcast() to send a packet to all
|
||||
connected peers on a given host over a specified channel id, as with
|
||||
enet_peer_send().
|
||||
|
||||
Queued packets will be sent on a call to enet_host_service().
|
||||
Alternatively, enet_host_flush() will send out queued packets without
|
||||
dispatching any events.
|
||||
|
||||
@code
|
||||
/* Create a reliable packet of size 7 containing "packet\0" */
|
||||
ENetPacket * packet = enet_packet_create ("packet",
|
||||
strlen ("packet") + 1,
|
||||
ENET_PACKET_FLAG_RELIABLE);
|
||||
|
||||
/* Extend the packet so and append the string "foo", so it now */
|
||||
/* contains "packetfoo\0" */
|
||||
enet_packet_resize (packet, strlen ("packetfoo") + 1);
|
||||
strcpy (& packet -> data [strlen ("packet")], "foo");
|
||||
|
||||
/* Send the packet to the peer over channel id 0. */
|
||||
/* One could also broadcast the packet by */
|
||||
/* enet_host_broadcast (host, 0, packet); */
|
||||
enet_peer_send (peer, 0, packet);
|
||||
...
|
||||
...
|
||||
...
|
||||
/* One could just use enet_host_service() instead. */
|
||||
enet_host_flush (host);
|
||||
@endcode
|
||||
|
||||
@section Disconnecting Disconnecting an ENet peer
|
||||
|
||||
Peers may be gently disconnected with enet_peer_disconnect(). A
|
||||
disconnect request will be sent to the foreign host, and ENet will
|
||||
wait for an acknowledgement from the foreign host before finally
|
||||
disconnecting. An event of type ENET_EVENT_TYPE_DISCONNECT will be
|
||||
generated once the disconnection succeeds. Normally timeouts apply to
|
||||
the disconnect acknowledgement, and so if no acknowledgement is
|
||||
received after a length of time the peer will be forcefully
|
||||
disconnected.
|
||||
|
||||
enet_peer_reset() will forcefully disconnect a peer. The foreign host
|
||||
will get no notification of a disconnect and will time out on the
|
||||
foreign host. No event is generated.
|
||||
|
||||
@code
|
||||
ENetEvent event;
|
||||
|
||||
enet_peer_disconnect (peer, 0);
|
||||
|
||||
/* Allow up to 3 seconds for the disconnect to succeed
|
||||
* and drop any packets received packets.
|
||||
*/
|
||||
while (enet_host_service (client, & event, 3000) > 0)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
enet_packet_destroy (event.packet);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
puts ("Disconnection succeeded.");
|
||||
return;
|
||||
...
|
||||
...
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
/* We've arrived here, so the disconnect attempt didn't */
|
||||
/* succeed yet. Force the connection down. */
|
||||
enet_peer_reset (peer);
|
||||
...
|
||||
...
|
||||
...
|
||||
@endcode
|
||||
|
||||
@section Connecting Connecting to an ENet host
|
||||
|
||||
A connection to a foreign host is initiated with enet_host_connect().
|
||||
It accepts the address of a foreign host to connect to, and the number
|
||||
of channels that should be allocated for communication. If N channels
|
||||
are allocated for use, their channel ids will be numbered 0 through
|
||||
N-1. A peer representing the connection attempt is returned, or NULL
|
||||
if there were no available peers over which to initiate the
|
||||
connection. When the connection attempt succeeds, an event of type
|
||||
ENET_EVENT_TYPE_CONNECT will be generated. If the connection attempt
|
||||
times out or otherwise fails, an event of type
|
||||
ENET_EVENT_TYPE_DISCONNECT will be generated.
|
||||
|
||||
@code
|
||||
ENetAddress address;
|
||||
ENetEvent event;
|
||||
ENetPeer *peer;
|
||||
|
||||
/* Connect to some.server.net:1234. */
|
||||
enet_address_set_host (& address, "some.server.net");
|
||||
address.port = 1234;
|
||||
|
||||
/* Initiate the connection, allocating the two channels 0 and 1. */
|
||||
peer = enet_host_connect (client, & address, 2, 0);
|
||||
|
||||
if (peer == NULL)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"No available peers for initiating an ENet connection.\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Wait up to 5 seconds for the connection attempt to succeed. */
|
||||
if (enet_host_service (client, & event, 5000) > 0 &&
|
||||
event.type == ENET_EVENT_TYPE_CONNECT)
|
||||
{
|
||||
puts ("Connection to some.server.net:1234 succeeded.");
|
||||
...
|
||||
...
|
||||
...
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Either the 5 seconds are up or a disconnect event was */
|
||||
/* received. Reset the peer in the event the 5 seconds */
|
||||
/* had run out without any significant event. */
|
||||
enet_peer_reset (peer);
|
||||
|
||||
puts ("Connection to some.server.net:1234 failed.");
|
||||
}
|
||||
...
|
||||
...
|
||||
...
|
||||
@endcode
|
||||
*/
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
# Microsoft Developer Studio Project File - Name="enet" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||
|
||||
CFG=enet - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "enet.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "enet.mak" CFG="enet - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "enet - Win32 Release" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "enet - Win32 Debug" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "enet - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
MTL=midl.exe
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
||||
# ADD CPP /nologo /W3 /O2 /I "include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ELSEIF "$(CFG)" == "enet - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
MTL=midl.exe
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /G6 /MTd /W3 /ZI /Od /I "include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "enet - Win32 Release"
|
||||
# Name "enet - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\host.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\list.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\callbacks.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\compress.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\packet.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\peer.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\protocol.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\unix.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\win32.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\enet.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\list.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\callbacks.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\protocol.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\time.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\types.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\unix.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\utility.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\include\enet\win32.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="enet_dll" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="bin\Debug\libenet" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Debug\" />
|
||||
<Option type="3" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Option createStaticLib="1" />
|
||||
<Compiler>
|
||||
<Add option="-g" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="bin\Release\libenet" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Release\" />
|
||||
<Option type="3" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Option createStaticLib="1" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-DENET_DLL" />
|
||||
<Add directory="include" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add library="ws2_32" />
|
||||
<Add library="Winmm" />
|
||||
</Linker>
|
||||
<Unit filename="callbacks.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="compress.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="host.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="include\enet\callbacks.h" />
|
||||
<Unit filename="include\enet\enet.h" />
|
||||
<Unit filename="include\enet\list.h" />
|
||||
<Unit filename="include\enet\protocol.h" />
|
||||
<Unit filename="include\enet\time.h" />
|
||||
<Unit filename="include\enet\types.h" />
|
||||
<Unit filename="include\enet\unix.h" />
|
||||
<Unit filename="include\enet\utility.h" />
|
||||
<Unit filename="include\enet\win32.h" />
|
||||
<Unit filename="list.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="packet.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="peer.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="protocol.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="unix.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="win32.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
|
|
@ -0,0 +1,503 @@
|
|||
/**
|
||||
@file host.c
|
||||
@brief ENet host management functions
|
||||
*/
|
||||
#define ENET_BUILDING_LIB 1
|
||||
#include <string.h>
|
||||
#include "enet/enet.h"
|
||||
|
||||
/** @defgroup host ENet host functions
|
||||
@{
|
||||
*/
|
||||
|
||||
/** Creates a host for communicating to peers.
|
||||
|
||||
@param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host.
|
||||
@param peerCount the maximum number of peers that should be allocated for the host.
|
||||
@param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
|
||||
@param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
|
||||
@param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
|
||||
|
||||
@returns the host on success and NULL on failure
|
||||
|
||||
@remarks ENet will strategically drop packets on specific sides of a connection between hosts
|
||||
to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine
|
||||
the window size of a connection which limits the amount of reliable packets that may be in transit
|
||||
at any given time.
|
||||
*/
|
||||
ENetHost *
|
||||
enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
|
||||
{
|
||||
ENetHost * host;
|
||||
ENetPeer * currentPeer;
|
||||
|
||||
if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID)
|
||||
return NULL;
|
||||
|
||||
host = (ENetHost *) enet_malloc (sizeof (ENetHost));
|
||||
if (host == NULL)
|
||||
return NULL;
|
||||
memset (host, 0, sizeof (ENetHost));
|
||||
|
||||
host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer));
|
||||
if (host -> peers == NULL)
|
||||
{
|
||||
enet_free (host);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
memset (host -> peers, 0, peerCount * sizeof (ENetPeer));
|
||||
|
||||
host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM);
|
||||
if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0))
|
||||
{
|
||||
if (host -> socket != ENET_SOCKET_NULL)
|
||||
enet_socket_destroy (host -> socket);
|
||||
|
||||
enet_free (host -> peers);
|
||||
enet_free (host);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1);
|
||||
enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1);
|
||||
enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
|
||||
enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
|
||||
|
||||
if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0)
|
||||
host -> address = * address;
|
||||
|
||||
if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
|
||||
channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
|
||||
else
|
||||
if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
|
||||
channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
|
||||
|
||||
host -> randomSeed = (enet_uint32) (size_t) host;
|
||||
host -> randomSeed += enet_host_random_seed ();
|
||||
host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16);
|
||||
host -> channelLimit = channelLimit;
|
||||
host -> incomingBandwidth = incomingBandwidth;
|
||||
host -> outgoingBandwidth = outgoingBandwidth;
|
||||
host -> bandwidthThrottleEpoch = 0;
|
||||
host -> recalculateBandwidthLimits = 0;
|
||||
host -> mtu = ENET_HOST_DEFAULT_MTU;
|
||||
host -> peerCount = peerCount;
|
||||
host -> commandCount = 0;
|
||||
host -> bufferCount = 0;
|
||||
host -> checksum = NULL;
|
||||
host -> receivedAddress.host = ENET_HOST_ANY;
|
||||
host -> receivedAddress.port = 0;
|
||||
host -> receivedData = NULL;
|
||||
host -> receivedDataLength = 0;
|
||||
|
||||
host -> totalSentData = 0;
|
||||
host -> totalSentPackets = 0;
|
||||
host -> totalReceivedData = 0;
|
||||
host -> totalReceivedPackets = 0;
|
||||
host -> totalQueued = 0;
|
||||
|
||||
host -> connectedPeers = 0;
|
||||
host -> bandwidthLimitedPeers = 0;
|
||||
host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID;
|
||||
host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE;
|
||||
host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA;
|
||||
|
||||
host -> compressor.context = NULL;
|
||||
host -> compressor.compress = NULL;
|
||||
host -> compressor.decompress = NULL;
|
||||
host -> compressor.destroy = NULL;
|
||||
|
||||
host -> intercept = NULL;
|
||||
|
||||
enet_list_clear (& host -> dispatchQueue);
|
||||
|
||||
for (currentPeer = host -> peers;
|
||||
currentPeer < & host -> peers [host -> peerCount];
|
||||
++ currentPeer)
|
||||
{
|
||||
currentPeer -> host = host;
|
||||
currentPeer -> incomingPeerID = currentPeer - host -> peers;
|
||||
currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF;
|
||||
currentPeer -> data = NULL;
|
||||
|
||||
enet_list_clear (& currentPeer -> acknowledgements);
|
||||
enet_list_clear (& currentPeer -> sentReliableCommands);
|
||||
enet_list_clear (& currentPeer -> outgoingCommands);
|
||||
enet_list_clear (& currentPeer -> outgoingSendReliableCommands);
|
||||
enet_list_clear (& currentPeer -> dispatchedCommands);
|
||||
|
||||
enet_peer_reset (currentPeer);
|
||||
}
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
/** Destroys the host and all resources associated with it.
|
||||
@param host pointer to the host to destroy
|
||||
*/
|
||||
void
|
||||
enet_host_destroy (ENetHost * host)
|
||||
{
|
||||
ENetPeer * currentPeer;
|
||||
|
||||
if (host == NULL)
|
||||
return;
|
||||
|
||||
enet_socket_destroy (host -> socket);
|
||||
|
||||
for (currentPeer = host -> peers;
|
||||
currentPeer < & host -> peers [host -> peerCount];
|
||||
++ currentPeer)
|
||||
{
|
||||
enet_peer_reset (currentPeer);
|
||||
}
|
||||
|
||||
if (host -> compressor.context != NULL && host -> compressor.destroy)
|
||||
(* host -> compressor.destroy) (host -> compressor.context);
|
||||
|
||||
enet_free (host -> peers);
|
||||
enet_free (host);
|
||||
}
|
||||
|
||||
enet_uint32
|
||||
enet_host_random (ENetHost * host)
|
||||
{
|
||||
/* Mulberry32 by Tommy Ettinger */
|
||||
enet_uint32 n = (host -> randomSeed += 0x6D2B79F5U);
|
||||
n = (n ^ (n >> 15)) * (n | 1U);
|
||||
n ^= n + (n ^ (n >> 7)) * (n | 61U);
|
||||
return n ^ (n >> 14);
|
||||
}
|
||||
|
||||
/** Initiates a connection to a foreign host.
|
||||
@param host host seeking the connection
|
||||
@param address destination for the connection
|
||||
@param channelCount number of channels to allocate
|
||||
@param data user data supplied to the receiving host
|
||||
@returns a peer representing the foreign host on success, NULL on failure
|
||||
@remarks The peer returned will have not completed the connection until enet_host_service()
|
||||
notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
|
||||
*/
|
||||
ENetPeer *
|
||||
enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data)
|
||||
{
|
||||
ENetPeer * currentPeer;
|
||||
ENetChannel * channel;
|
||||
ENetProtocol command;
|
||||
|
||||
if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
|
||||
channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
|
||||
else
|
||||
if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
|
||||
channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
|
||||
|
||||
for (currentPeer = host -> peers;
|
||||
currentPeer < & host -> peers [host -> peerCount];
|
||||
++ currentPeer)
|
||||
{
|
||||
if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentPeer >= & host -> peers [host -> peerCount])
|
||||
return NULL;
|
||||
|
||||
currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
|
||||
if (currentPeer -> channels == NULL)
|
||||
return NULL;
|
||||
currentPeer -> channelCount = channelCount;
|
||||
currentPeer -> state = ENET_PEER_STATE_CONNECTING;
|
||||
currentPeer -> address = * address;
|
||||
currentPeer -> connectID = enet_host_random (host);
|
||||
currentPeer -> mtu = host -> mtu;
|
||||
|
||||
if (host -> outgoingBandwidth == 0)
|
||||
currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
|
||||
else
|
||||
currentPeer -> windowSize = (host -> outgoingBandwidth /
|
||||
ENET_PEER_WINDOW_SIZE_SCALE) *
|
||||
ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
|
||||
|
||||
if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
|
||||
currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
|
||||
else
|
||||
if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
|
||||
currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
|
||||
|
||||
for (channel = currentPeer -> channels;
|
||||
channel < & currentPeer -> channels [channelCount];
|
||||
++ channel)
|
||||
{
|
||||
channel -> outgoingReliableSequenceNumber = 0;
|
||||
channel -> outgoingUnreliableSequenceNumber = 0;
|
||||
channel -> incomingReliableSequenceNumber = 0;
|
||||
channel -> incomingUnreliableSequenceNumber = 0;
|
||||
|
||||
enet_list_clear (& channel -> incomingReliableCommands);
|
||||
enet_list_clear (& channel -> incomingUnreliableCommands);
|
||||
|
||||
channel -> usedReliableWindows = 0;
|
||||
memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
|
||||
}
|
||||
|
||||
command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
|
||||
command.header.channelID = 0xFF;
|
||||
command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
|
||||
command.connect.incomingSessionID = currentPeer -> incomingSessionID;
|
||||
command.connect.outgoingSessionID = currentPeer -> outgoingSessionID;
|
||||
command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu);
|
||||
command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
|
||||
command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
|
||||
command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
|
||||
command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
|
||||
command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
|
||||
command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
|
||||
command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
|
||||
command.connect.connectID = currentPeer -> connectID;
|
||||
command.connect.data = ENET_HOST_TO_NET_32 (data);
|
||||
|
||||
enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
|
||||
|
||||
return currentPeer;
|
||||
}
|
||||
|
||||
/** Queues a packet to be sent to all peers associated with the host.
|
||||
@param host host on which to broadcast the packet
|
||||
@param channelID channel on which to broadcast
|
||||
@param packet packet to broadcast
|
||||
*/
|
||||
void
|
||||
enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
|
||||
{
|
||||
ENetPeer * currentPeer;
|
||||
|
||||
for (currentPeer = host -> peers;
|
||||
currentPeer < & host -> peers [host -> peerCount];
|
||||
++ currentPeer)
|
||||
{
|
||||
if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
|
||||
continue;
|
||||
|
||||
enet_peer_send (currentPeer, channelID, packet);
|
||||
}
|
||||
|
||||
if (packet -> referenceCount == 0)
|
||||
enet_packet_destroy (packet);
|
||||
}
|
||||
|
||||
/** Sets the packet compressor the host should use to compress and decompress packets.
|
||||
@param host host to enable or disable compression for
|
||||
@param compressor callbacks for for the packet compressor; if NULL, then compression is disabled
|
||||
*/
|
||||
void
|
||||
enet_host_compress (ENetHost * host, const ENetCompressor * compressor)
|
||||
{
|
||||
if (host -> compressor.context != NULL && host -> compressor.destroy)
|
||||
(* host -> compressor.destroy) (host -> compressor.context);
|
||||
|
||||
if (compressor)
|
||||
host -> compressor = * compressor;
|
||||
else
|
||||
host -> compressor.context = NULL;
|
||||
}
|
||||
|
||||
/** Limits the maximum allowed channels of future incoming connections.
|
||||
@param host host to limit
|
||||
@param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
|
||||
*/
|
||||
void
|
||||
enet_host_channel_limit (ENetHost * host, size_t channelLimit)
|
||||
{
|
||||
if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
|
||||
channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
|
||||
else
|
||||
if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
|
||||
channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
|
||||
|
||||
host -> channelLimit = channelLimit;
|
||||
}
|
||||
|
||||
|
||||
/** Adjusts the bandwidth limits of a host.
|
||||
@param host host to adjust
|
||||
@param incomingBandwidth new incoming bandwidth
|
||||
@param outgoingBandwidth new outgoing bandwidth
|
||||
@remarks the incoming and outgoing bandwidth parameters are identical in function to those
|
||||
specified in enet_host_create().
|
||||
*/
|
||||
void
|
||||
enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
|
||||
{
|
||||
host -> incomingBandwidth = incomingBandwidth;
|
||||
host -> outgoingBandwidth = outgoingBandwidth;
|
||||
host -> recalculateBandwidthLimits = 1;
|
||||
}
|
||||
|
||||
void
|
||||
enet_host_bandwidth_throttle (ENetHost * host)
|
||||
{
|
||||
enet_uint32 timeCurrent = enet_time_get (),
|
||||
elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
|
||||
peersRemaining = (enet_uint32) host -> connectedPeers,
|
||||
dataTotal = ~0,
|
||||
bandwidth = ~0,
|
||||
throttle = 0,
|
||||
bandwidthLimit = 0;
|
||||
int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0;
|
||||
ENetPeer * peer;
|
||||
ENetProtocol command;
|
||||
|
||||
if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
|
||||
return;
|
||||
|
||||
host -> bandwidthThrottleEpoch = timeCurrent;
|
||||
|
||||
if (peersRemaining == 0)
|
||||
return;
|
||||
|
||||
if (host -> outgoingBandwidth != 0)
|
||||
{
|
||||
dataTotal = 0;
|
||||
bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
|
||||
|
||||
for (peer = host -> peers;
|
||||
peer < & host -> peers [host -> peerCount];
|
||||
++ peer)
|
||||
{
|
||||
if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
|
||||
continue;
|
||||
|
||||
dataTotal += peer -> outgoingDataTotal;
|
||||
}
|
||||
}
|
||||
|
||||
while (peersRemaining > 0 && needsAdjustment != 0)
|
||||
{
|
||||
needsAdjustment = 0;
|
||||
|
||||
if (dataTotal <= bandwidth)
|
||||
throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
|
||||
else
|
||||
throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
|
||||
|
||||
for (peer = host -> peers;
|
||||
peer < & host -> peers [host -> peerCount];
|
||||
++ peer)
|
||||
{
|
||||
enet_uint32 peerBandwidth;
|
||||
|
||||
if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
|
||||
peer -> incomingBandwidth == 0 ||
|
||||
peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
|
||||
continue;
|
||||
|
||||
peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
|
||||
if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
|
||||
continue;
|
||||
|
||||
peer -> packetThrottleLimit = (peerBandwidth *
|
||||
ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
|
||||
|
||||
if (peer -> packetThrottleLimit == 0)
|
||||
peer -> packetThrottleLimit = 1;
|
||||
|
||||
if (peer -> packetThrottle > peer -> packetThrottleLimit)
|
||||
peer -> packetThrottle = peer -> packetThrottleLimit;
|
||||
|
||||
peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
|
||||
|
||||
peer -> incomingDataTotal = 0;
|
||||
peer -> outgoingDataTotal = 0;
|
||||
|
||||
needsAdjustment = 1;
|
||||
-- peersRemaining;
|
||||
bandwidth -= peerBandwidth;
|
||||
dataTotal -= peerBandwidth;
|
||||
}
|
||||
}
|
||||
|
||||
if (peersRemaining > 0)
|
||||
{
|
||||
if (dataTotal <= bandwidth)
|
||||
throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
|
||||
else
|
||||
throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
|
||||
|
||||
for (peer = host -> peers;
|
||||
peer < & host -> peers [host -> peerCount];
|
||||
++ peer)
|
||||
{
|
||||
if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
|
||||
peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
|
||||
continue;
|
||||
|
||||
peer -> packetThrottleLimit = throttle;
|
||||
|
||||
if (peer -> packetThrottle > peer -> packetThrottleLimit)
|
||||
peer -> packetThrottle = peer -> packetThrottleLimit;
|
||||
|
||||
peer -> incomingDataTotal = 0;
|
||||
peer -> outgoingDataTotal = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (host -> recalculateBandwidthLimits)
|
||||
{
|
||||
host -> recalculateBandwidthLimits = 0;
|
||||
|
||||
peersRemaining = (enet_uint32) host -> connectedPeers;
|
||||
bandwidth = host -> incomingBandwidth;
|
||||
needsAdjustment = 1;
|
||||
|
||||
if (bandwidth == 0)
|
||||
bandwidthLimit = 0;
|
||||
else
|
||||
while (peersRemaining > 0 && needsAdjustment != 0)
|
||||
{
|
||||
needsAdjustment = 0;
|
||||
bandwidthLimit = bandwidth / peersRemaining;
|
||||
|
||||
for (peer = host -> peers;
|
||||
peer < & host -> peers [host -> peerCount];
|
||||
++ peer)
|
||||
{
|
||||
if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
|
||||
peer -> incomingBandwidthThrottleEpoch == timeCurrent)
|
||||
continue;
|
||||
|
||||
if (peer -> outgoingBandwidth > 0 &&
|
||||
peer -> outgoingBandwidth >= bandwidthLimit)
|
||||
continue;
|
||||
|
||||
peer -> incomingBandwidthThrottleEpoch = timeCurrent;
|
||||
|
||||
needsAdjustment = 1;
|
||||
-- peersRemaining;
|
||||
bandwidth -= peer -> outgoingBandwidth;
|
||||
}
|
||||
}
|
||||
|
||||
for (peer = host -> peers;
|
||||
peer < & host -> peers [host -> peerCount];
|
||||
++ peer)
|
||||
{
|
||||
if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
|
||||
continue;
|
||||
|
||||
command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
|
||||
command.header.channelID = 0xFF;
|
||||
command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
|
||||
|
||||
if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
|
||||
command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
|
||||
else
|
||||
command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
|
||||
|
||||
enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
@file callbacks.h
|
||||
@brief ENet callbacks
|
||||
*/
|
||||
#ifndef __ENET_CALLBACKS_H__
|
||||
#define __ENET_CALLBACKS_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct _ENetCallbacks
|
||||
{
|
||||
void * (ENET_CALLBACK * malloc) (size_t size);
|
||||
void (ENET_CALLBACK * free) (void * memory);
|
||||
void (ENET_CALLBACK * no_memory) (void);
|
||||
} ENetCallbacks;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** @defgroup callbacks ENet internal callbacks
|
||||
@{
|
||||
@ingroup private
|
||||
*/
|
||||
|
||||
extern void * enet_malloc (size_t);
|
||||
extern void enet_free (void *);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ENET_CALLBACKS_H__ */
|
||||
|
||||
|
|
@ -0,0 +1,615 @@
|
|||
/**
|
||||
@file enet.h
|
||||
@brief ENet public header file
|
||||
*/
|
||||
#ifndef __ENET_ENET_H__
|
||||
#define __ENET_ENET_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "enet/win32.h"
|
||||
#else
|
||||
#include "enet/unix.h"
|
||||
#endif
|
||||
|
||||
#include "enet/types.h"
|
||||
#include "enet/protocol.h"
|
||||
#include "enet/list.h"
|
||||
#include "enet/callbacks.h"
|
||||
|
||||
#define ENET_VERSION_MAJOR 1
|
||||
#define ENET_VERSION_MINOR 3
|
||||
#define ENET_VERSION_PATCH 18
|
||||
#define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch))
|
||||
#define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF)
|
||||
#define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF)
|
||||
#define ENET_VERSION_GET_PATCH(version) ((version)&0xFF)
|
||||
#define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH)
|
||||
|
||||
typedef enet_uint32 ENetVersion;
|
||||
|
||||
struct _ENetHost;
|
||||
struct _ENetEvent;
|
||||
struct _ENetPacket;
|
||||
|
||||
typedef enum _ENetSocketType
|
||||
{
|
||||
ENET_SOCKET_TYPE_STREAM = 1,
|
||||
ENET_SOCKET_TYPE_DATAGRAM = 2
|
||||
} ENetSocketType;
|
||||
|
||||
typedef enum _ENetSocketWait
|
||||
{
|
||||
ENET_SOCKET_WAIT_NONE = 0,
|
||||
ENET_SOCKET_WAIT_SEND = (1 << 0),
|
||||
ENET_SOCKET_WAIT_RECEIVE = (1 << 1),
|
||||
ENET_SOCKET_WAIT_INTERRUPT = (1 << 2)
|
||||
} ENetSocketWait;
|
||||
|
||||
typedef enum _ENetSocketOption
|
||||
{
|
||||
ENET_SOCKOPT_NONBLOCK = 1,
|
||||
ENET_SOCKOPT_BROADCAST = 2,
|
||||
ENET_SOCKOPT_RCVBUF = 3,
|
||||
ENET_SOCKOPT_SNDBUF = 4,
|
||||
ENET_SOCKOPT_REUSEADDR = 5,
|
||||
ENET_SOCKOPT_RCVTIMEO = 6,
|
||||
ENET_SOCKOPT_SNDTIMEO = 7,
|
||||
ENET_SOCKOPT_ERROR = 8,
|
||||
ENET_SOCKOPT_NODELAY = 9,
|
||||
ENET_SOCKOPT_TTL = 10
|
||||
} ENetSocketOption;
|
||||
|
||||
typedef enum _ENetSocketShutdown
|
||||
{
|
||||
ENET_SOCKET_SHUTDOWN_READ = 0,
|
||||
ENET_SOCKET_SHUTDOWN_WRITE = 1,
|
||||
ENET_SOCKET_SHUTDOWN_READ_WRITE = 2
|
||||
} ENetSocketShutdown;
|
||||
|
||||
#define ENET_HOST_ANY 0
|
||||
#define ENET_HOST_BROADCAST 0xFFFFFFFFU
|
||||
#define ENET_PORT_ANY 0
|
||||
|
||||
/**
|
||||
* Portable internet address structure.
|
||||
*
|
||||
* The host must be specified in network byte-order, and the port must be in host
|
||||
* byte-order. The constant ENET_HOST_ANY may be used to specify the default
|
||||
* server host. The constant ENET_HOST_BROADCAST may be used to specify the
|
||||
* broadcast address (255.255.255.255). This makes sense for enet_host_connect,
|
||||
* but not for enet_host_create. Once a server responds to a broadcast, the
|
||||
* address is updated from ENET_HOST_BROADCAST to the server's actual IP address.
|
||||
*/
|
||||
typedef struct _ENetAddress
|
||||
{
|
||||
enet_uint32 host;
|
||||
enet_uint16 port;
|
||||
} ENetAddress;
|
||||
|
||||
/**
|
||||
* Packet flag bit constants.
|
||||
*
|
||||
* The host must be specified in network byte-order, and the port must be in
|
||||
* host byte-order. The constant ENET_HOST_ANY may be used to specify the
|
||||
* default server host.
|
||||
|
||||
@sa ENetPacket
|
||||
*/
|
||||
typedef enum _ENetPacketFlag
|
||||
{
|
||||
/** packet must be received by the target peer and resend attempts should be
|
||||
* made until the packet is delivered */
|
||||
ENET_PACKET_FLAG_RELIABLE = (1 << 0),
|
||||
/** packet will not be sequenced with other packets
|
||||
*/
|
||||
ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1),
|
||||
/** packet will not allocate data, and user must supply it instead */
|
||||
ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2),
|
||||
/** packet will be fragmented using unreliable (instead of reliable) sends
|
||||
* if it exceeds the MTU */
|
||||
ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3),
|
||||
|
||||
/** whether the packet has been sent from all queues it has been entered into */
|
||||
ENET_PACKET_FLAG_SENT = (1<<8)
|
||||
} ENetPacketFlag;
|
||||
|
||||
typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *);
|
||||
|
||||
/**
|
||||
* ENet packet structure.
|
||||
*
|
||||
* An ENet data packet that may be sent to or received from a peer. The shown
|
||||
* fields should only be read and never modified. The data field contains the
|
||||
* allocated data for the packet. The dataLength fields specifies the length
|
||||
* of the allocated data. The flags field is either 0 (specifying no flags),
|
||||
* or a bitwise-or of any combination of the following flags:
|
||||
*
|
||||
* ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer
|
||||
* and resend attempts should be made until the packet is delivered
|
||||
*
|
||||
* ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets
|
||||
* (not supported for reliable packets)
|
||||
*
|
||||
* ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead
|
||||
*
|
||||
* ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable
|
||||
* (instead of reliable) sends if it exceeds the MTU
|
||||
*
|
||||
* ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into
|
||||
@sa ENetPacketFlag
|
||||
*/
|
||||
typedef struct _ENetPacket
|
||||
{
|
||||
size_t referenceCount; /**< internal use only */
|
||||
enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */
|
||||
enet_uint8 * data; /**< allocated data for packet */
|
||||
size_t dataLength; /**< length of data */
|
||||
ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */
|
||||
void * userData; /**< application private data, may be freely modified */
|
||||
} ENetPacket;
|
||||
|
||||
typedef struct _ENetAcknowledgement
|
||||
{
|
||||
ENetListNode acknowledgementList;
|
||||
enet_uint32 sentTime;
|
||||
ENetProtocol command;
|
||||
} ENetAcknowledgement;
|
||||
|
||||
typedef struct _ENetOutgoingCommand
|
||||
{
|
||||
ENetListNode outgoingCommandList;
|
||||
enet_uint16 reliableSequenceNumber;
|
||||
enet_uint16 unreliableSequenceNumber;
|
||||
enet_uint32 sentTime;
|
||||
enet_uint32 roundTripTimeout;
|
||||
enet_uint32 queueTime;
|
||||
enet_uint32 fragmentOffset;
|
||||
enet_uint16 fragmentLength;
|
||||
enet_uint16 sendAttempts;
|
||||
ENetProtocol command;
|
||||
ENetPacket * packet;
|
||||
} ENetOutgoingCommand;
|
||||
|
||||
typedef struct _ENetIncomingCommand
|
||||
{
|
||||
ENetListNode incomingCommandList;
|
||||
enet_uint16 reliableSequenceNumber;
|
||||
enet_uint16 unreliableSequenceNumber;
|
||||
ENetProtocol command;
|
||||
enet_uint32 fragmentCount;
|
||||
enet_uint32 fragmentsRemaining;
|
||||
enet_uint32 * fragments;
|
||||
ENetPacket * packet;
|
||||
} ENetIncomingCommand;
|
||||
|
||||
typedef enum _ENetPeerState
|
||||
{
|
||||
ENET_PEER_STATE_DISCONNECTED = 0,
|
||||
ENET_PEER_STATE_CONNECTING = 1,
|
||||
ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2,
|
||||
ENET_PEER_STATE_CONNECTION_PENDING = 3,
|
||||
ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4,
|
||||
ENET_PEER_STATE_CONNECTED = 5,
|
||||
ENET_PEER_STATE_DISCONNECT_LATER = 6,
|
||||
ENET_PEER_STATE_DISCONNECTING = 7,
|
||||
ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8,
|
||||
ENET_PEER_STATE_ZOMBIE = 9
|
||||
} ENetPeerState;
|
||||
|
||||
#ifndef ENET_BUFFER_MAXIMUM
|
||||
#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS)
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024,
|
||||
ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024,
|
||||
ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000,
|
||||
ENET_HOST_DEFAULT_MTU = 1392,
|
||||
ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024,
|
||||
ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024,
|
||||
|
||||
ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500,
|
||||
ENET_PEER_DEFAULT_PACKET_THROTTLE = 32,
|
||||
ENET_PEER_PACKET_THROTTLE_SCALE = 32,
|
||||
ENET_PEER_PACKET_THROTTLE_COUNTER = 7,
|
||||
ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2,
|
||||
ENET_PEER_PACKET_THROTTLE_DECELERATION = 2,
|
||||
ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000,
|
||||
ENET_PEER_PACKET_LOSS_SCALE = (1 << 16),
|
||||
ENET_PEER_PACKET_LOSS_INTERVAL = 10000,
|
||||
ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024,
|
||||
ENET_PEER_TIMEOUT_LIMIT = 32,
|
||||
ENET_PEER_TIMEOUT_MINIMUM = 5000,
|
||||
ENET_PEER_TIMEOUT_MAXIMUM = 30000,
|
||||
ENET_PEER_PING_INTERVAL = 500,
|
||||
ENET_PEER_UNSEQUENCED_WINDOWS = 64,
|
||||
ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024,
|
||||
ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32,
|
||||
ENET_PEER_RELIABLE_WINDOWS = 16,
|
||||
ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000,
|
||||
ENET_PEER_FREE_RELIABLE_WINDOWS = 8
|
||||
};
|
||||
|
||||
typedef struct _ENetChannel
|
||||
{
|
||||
enet_uint16 outgoingReliableSequenceNumber;
|
||||
enet_uint16 outgoingUnreliableSequenceNumber;
|
||||
enet_uint16 usedReliableWindows;
|
||||
enet_uint16 reliableWindows [ENET_PEER_RELIABLE_WINDOWS];
|
||||
enet_uint16 incomingReliableSequenceNumber;
|
||||
enet_uint16 incomingUnreliableSequenceNumber;
|
||||
ENetList incomingReliableCommands;
|
||||
ENetList incomingUnreliableCommands;
|
||||
} ENetChannel;
|
||||
|
||||
typedef enum _ENetPeerFlag
|
||||
{
|
||||
ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0),
|
||||
ENET_PEER_FLAG_CONTINUE_SENDING = (1 << 1)
|
||||
} ENetPeerFlag;
|
||||
|
||||
/**
|
||||
* An ENet peer which data packets may be sent or received from.
|
||||
*
|
||||
* No fields should be modified unless otherwise specified.
|
||||
*/
|
||||
typedef struct _ENetPeer
|
||||
{
|
||||
ENetListNode dispatchList;
|
||||
struct _ENetHost * host;
|
||||
enet_uint16 outgoingPeerID;
|
||||
enet_uint16 incomingPeerID;
|
||||
enet_uint32 connectID;
|
||||
enet_uint8 outgoingSessionID;
|
||||
enet_uint8 incomingSessionID;
|
||||
ENetAddress address; /**< Internet address of the peer */
|
||||
void * data; /**< Application private data, may be freely modified */
|
||||
ENetPeerState state;
|
||||
ENetChannel * channels;
|
||||
size_t channelCount; /**< Number of channels allocated for communication with peer */
|
||||
enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */
|
||||
enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */
|
||||
enet_uint32 incomingBandwidthThrottleEpoch;
|
||||
enet_uint32 outgoingBandwidthThrottleEpoch;
|
||||
enet_uint32 incomingDataTotal;
|
||||
enet_uint32 outgoingDataTotal;
|
||||
enet_uint32 lastSendTime;
|
||||
enet_uint32 lastReceiveTime;
|
||||
enet_uint32 nextTimeout;
|
||||
enet_uint32 earliestTimeout;
|
||||
enet_uint32 packetLossEpoch;
|
||||
enet_uint32 packetsSent;
|
||||
enet_uint32 packetsLost;
|
||||
enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */
|
||||
enet_uint32 packetLossVariance;
|
||||
enet_uint32 packetThrottle;
|
||||
enet_uint32 packetThrottleLimit;
|
||||
enet_uint32 packetThrottleCounter;
|
||||
enet_uint32 packetThrottleEpoch;
|
||||
enet_uint32 packetThrottleAcceleration;
|
||||
enet_uint32 packetThrottleDeceleration;
|
||||
enet_uint32 packetThrottleInterval;
|
||||
enet_uint32 pingInterval;
|
||||
enet_uint32 timeoutLimit;
|
||||
enet_uint32 timeoutMinimum;
|
||||
enet_uint32 timeoutMaximum;
|
||||
enet_uint32 lastRoundTripTime;
|
||||
enet_uint32 lowestRoundTripTime;
|
||||
enet_uint32 lastRoundTripTimeVariance;
|
||||
enet_uint32 highestRoundTripTimeVariance;
|
||||
enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */
|
||||
enet_uint32 roundTripTimeVariance;
|
||||
enet_uint32 mtu;
|
||||
enet_uint32 windowSize;
|
||||
enet_uint32 reliableDataInTransit;
|
||||
enet_uint16 outgoingReliableSequenceNumber;
|
||||
ENetList acknowledgements;
|
||||
ENetList sentReliableCommands;
|
||||
ENetList outgoingSendReliableCommands;
|
||||
ENetList outgoingCommands;
|
||||
ENetList dispatchedCommands;
|
||||
enet_uint16 flags;
|
||||
enet_uint16 reserved;
|
||||
enet_uint16 incomingUnsequencedGroup;
|
||||
enet_uint16 outgoingUnsequencedGroup;
|
||||
enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32];
|
||||
enet_uint32 eventData;
|
||||
size_t totalWaitingData;
|
||||
} ENetPeer;
|
||||
|
||||
/** An ENet packet compressor for compressing UDP packets before socket sends or receives.
|
||||
*/
|
||||
typedef struct _ENetCompressor
|
||||
{
|
||||
/** Context data for the compressor. Must be non-NULL. */
|
||||
void * context;
|
||||
/** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */
|
||||
size_t (ENET_CALLBACK * compress) (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit);
|
||||
/** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */
|
||||
size_t (ENET_CALLBACK * decompress) (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit);
|
||||
/** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */
|
||||
void (ENET_CALLBACK * destroy) (void * context);
|
||||
} ENetCompressor;
|
||||
|
||||
/** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */
|
||||
typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback) (const ENetBuffer * buffers, size_t bufferCount);
|
||||
|
||||
/** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */
|
||||
typedef int (ENET_CALLBACK * ENetInterceptCallback) (struct _ENetHost * host, struct _ENetEvent * event);
|
||||
|
||||
/** An ENet host for communicating with peers.
|
||||
*
|
||||
* No fields should be modified unless otherwise stated.
|
||||
|
||||
@sa enet_host_create()
|
||||
@sa enet_host_destroy()
|
||||
@sa enet_host_connect()
|
||||
@sa enet_host_service()
|
||||
@sa enet_host_flush()
|
||||
@sa enet_host_broadcast()
|
||||
@sa enet_host_compress()
|
||||
@sa enet_host_compress_with_range_coder()
|
||||
@sa enet_host_channel_limit()
|
||||
@sa enet_host_bandwidth_limit()
|
||||
@sa enet_host_bandwidth_throttle()
|
||||
*/
|
||||
typedef struct _ENetHost
|
||||
{
|
||||
ENetSocket socket;
|
||||
ENetAddress address; /**< Internet address of the host */
|
||||
enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */
|
||||
enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */
|
||||
enet_uint32 bandwidthThrottleEpoch;
|
||||
enet_uint32 mtu;
|
||||
enet_uint32 randomSeed;
|
||||
int recalculateBandwidthLimits;
|
||||
ENetPeer * peers; /**< array of peers allocated for this host */
|
||||
size_t peerCount; /**< number of peers allocated for this host */
|
||||
size_t channelLimit; /**< maximum number of channels allowed for connected peers */
|
||||
enet_uint32 serviceTime;
|
||||
ENetList dispatchQueue;
|
||||
enet_uint32 totalQueued;
|
||||
size_t packetSize;
|
||||
enet_uint16 headerFlags;
|
||||
ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS];
|
||||
size_t commandCount;
|
||||
ENetBuffer buffers [ENET_BUFFER_MAXIMUM];
|
||||
size_t bufferCount;
|
||||
ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */
|
||||
ENetCompressor compressor;
|
||||
enet_uint8 packetData [2][ENET_PROTOCOL_MAXIMUM_MTU];
|
||||
ENetAddress receivedAddress;
|
||||
enet_uint8 * receivedData;
|
||||
size_t receivedDataLength;
|
||||
enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */
|
||||
enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */
|
||||
enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */
|
||||
enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */
|
||||
ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */
|
||||
size_t connectedPeers;
|
||||
size_t bandwidthLimitedPeers;
|
||||
size_t duplicatePeers; /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */
|
||||
size_t maximumPacketSize; /**< the maximum allowable packet size that may be sent or received on a peer */
|
||||
size_t maximumWaitingData; /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */
|
||||
} ENetHost;
|
||||
|
||||
/**
|
||||
* An ENet event type, as specified in @ref ENetEvent.
|
||||
*/
|
||||
typedef enum _ENetEventType
|
||||
{
|
||||
/** no event occurred within the specified time limit */
|
||||
ENET_EVENT_TYPE_NONE = 0,
|
||||
|
||||
/** a connection request initiated by enet_host_connect has completed.
|
||||
* The peer field contains the peer which successfully connected.
|
||||
*/
|
||||
ENET_EVENT_TYPE_CONNECT = 1,
|
||||
|
||||
/** a peer has disconnected. This event is generated on a successful
|
||||
* completion of a disconnect initiated by enet_peer_disconnect, if
|
||||
* a peer has timed out, or if a connection request intialized by
|
||||
* enet_host_connect has timed out. The peer field contains the peer
|
||||
* which disconnected. The data field contains user supplied data
|
||||
* describing the disconnection, or 0, if none is available.
|
||||
*/
|
||||
ENET_EVENT_TYPE_DISCONNECT = 2,
|
||||
|
||||
/** a packet has been received from a peer. The peer field specifies the
|
||||
* peer which sent the packet. The channelID field specifies the channel
|
||||
* number upon which the packet was received. The packet field contains
|
||||
* the packet that was received; this packet must be destroyed with
|
||||
* enet_packet_destroy after use.
|
||||
*/
|
||||
ENET_EVENT_TYPE_RECEIVE = 3
|
||||
} ENetEventType;
|
||||
|
||||
/**
|
||||
* An ENet event as returned by enet_host_service().
|
||||
|
||||
@sa enet_host_service
|
||||
*/
|
||||
typedef struct _ENetEvent
|
||||
{
|
||||
ENetEventType type; /**< type of the event */
|
||||
ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */
|
||||
enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */
|
||||
enet_uint32 data; /**< data associated with the event, if appropriate */
|
||||
ENetPacket * packet; /**< packet associated with the event, if appropriate */
|
||||
} ENetEvent;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** @defgroup global ENet global functions
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
Initializes ENet globally. Must be called prior to using any functions in
|
||||
ENet.
|
||||
@returns 0 on success, < 0 on failure
|
||||
*/
|
||||
ENET_API int enet_initialize (void);
|
||||
|
||||
/**
|
||||
Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored.
|
||||
|
||||
@param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use
|
||||
@param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults
|
||||
@returns 0 on success, < 0 on failure
|
||||
*/
|
||||
ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits);
|
||||
|
||||
/**
|
||||
Shuts down ENet globally. Should be called when a program that has
|
||||
initialized ENet exits.
|
||||
*/
|
||||
ENET_API void enet_deinitialize (void);
|
||||
|
||||
/**
|
||||
Gives the linked version of the ENet library.
|
||||
@returns the version number
|
||||
*/
|
||||
ENET_API ENetVersion enet_linked_version (void);
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @defgroup private ENet private implementation functions */
|
||||
|
||||
/**
|
||||
Returns the wall-time in milliseconds. Its initial value is unspecified
|
||||
unless otherwise set.
|
||||
*/
|
||||
ENET_API enet_uint32 enet_time_get (void);
|
||||
/**
|
||||
Sets the current wall-time in milliseconds.
|
||||
*/
|
||||
ENET_API void enet_time_set (enet_uint32);
|
||||
|
||||
/** @defgroup socket ENet socket functions
|
||||
@{
|
||||
*/
|
||||
ENET_API ENetSocket enet_socket_create (ENetSocketType);
|
||||
ENET_API int enet_socket_bind (ENetSocket, const ENetAddress *);
|
||||
ENET_API int enet_socket_get_address (ENetSocket, ENetAddress *);
|
||||
ENET_API int enet_socket_listen (ENetSocket, int);
|
||||
ENET_API ENetSocket enet_socket_accept (ENetSocket, ENetAddress *);
|
||||
ENET_API int enet_socket_connect (ENetSocket, const ENetAddress *);
|
||||
ENET_API int enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t);
|
||||
ENET_API int enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t);
|
||||
ENET_API int enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32);
|
||||
ENET_API int enet_socket_set_option (ENetSocket, ENetSocketOption, int);
|
||||
ENET_API int enet_socket_get_option (ENetSocket, ENetSocketOption, int *);
|
||||
ENET_API int enet_socket_shutdown (ENetSocket, ENetSocketShutdown);
|
||||
ENET_API void enet_socket_destroy (ENetSocket);
|
||||
ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32);
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @defgroup Address ENet address functions
|
||||
@{
|
||||
*/
|
||||
|
||||
/** Attempts to parse the printable form of the IP address in the parameter hostName
|
||||
and sets the host field in the address parameter if successful.
|
||||
@param address destination to store the parsed IP address
|
||||
@param hostName IP address to parse
|
||||
@retval 0 on success
|
||||
@retval < 0 on failure
|
||||
@returns the address of the given hostName in address on success
|
||||
*/
|
||||
ENET_API int enet_address_set_host_ip (ENetAddress * address, const char * hostName);
|
||||
|
||||
/** Attempts to resolve the host named by the parameter hostName and sets
|
||||
the host field in the address parameter if successful.
|
||||
@param address destination to store resolved address
|
||||
@param hostName host name to lookup
|
||||
@retval 0 on success
|
||||
@retval < 0 on failure
|
||||
@returns the address of the given hostName in address on success
|
||||
*/
|
||||
ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName);
|
||||
|
||||
/** Gives the printable form of the IP address specified in the address parameter.
|
||||
@param address address printed
|
||||
@param hostName destination for name, must not be NULL
|
||||
@param nameLength maximum length of hostName.
|
||||
@returns the null-terminated name of the host in hostName on success
|
||||
@retval 0 on success
|
||||
@retval < 0 on failure
|
||||
*/
|
||||
ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostName, size_t nameLength);
|
||||
|
||||
/** Attempts to do a reverse lookup of the host field in the address parameter.
|
||||
@param address address used for reverse lookup
|
||||
@param hostName destination for name, must not be NULL
|
||||
@param nameLength maximum length of hostName.
|
||||
@returns the null-terminated name of the host in hostName on success
|
||||
@retval 0 on success
|
||||
@retval < 0 on failure
|
||||
*/
|
||||
ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength);
|
||||
|
||||
/** @} */
|
||||
|
||||
ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32);
|
||||
ENET_API void enet_packet_destroy (ENetPacket *);
|
||||
ENET_API int enet_packet_resize (ENetPacket *, size_t);
|
||||
ENET_API enet_uint32 enet_crc32 (const ENetBuffer *, size_t);
|
||||
|
||||
ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32);
|
||||
ENET_API void enet_host_destroy (ENetHost *);
|
||||
ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32);
|
||||
ENET_API int enet_host_check_events (ENetHost *, ENetEvent *);
|
||||
ENET_API int enet_host_service (ENetHost *, ENetEvent *, enet_uint32);
|
||||
ENET_API void enet_host_flush (ENetHost *);
|
||||
ENET_API void enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *);
|
||||
ENET_API void enet_host_compress (ENetHost *, const ENetCompressor *);
|
||||
ENET_API int enet_host_compress_with_range_coder (ENetHost * host);
|
||||
ENET_API void enet_host_channel_limit (ENetHost *, size_t);
|
||||
ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32);
|
||||
extern void enet_host_bandwidth_throttle (ENetHost *);
|
||||
extern enet_uint32 enet_host_random_seed (void);
|
||||
extern enet_uint32 enet_host_random (ENetHost *);
|
||||
|
||||
ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *);
|
||||
ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID);
|
||||
ENET_API void enet_peer_ping (ENetPeer *);
|
||||
ENET_API void enet_peer_ping_interval (ENetPeer *, enet_uint32);
|
||||
ENET_API void enet_peer_timeout (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
|
||||
ENET_API void enet_peer_reset (ENetPeer *);
|
||||
ENET_API void enet_peer_disconnect (ENetPeer *, enet_uint32);
|
||||
ENET_API void enet_peer_disconnect_now (ENetPeer *, enet_uint32);
|
||||
ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32);
|
||||
ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
|
||||
extern int enet_peer_throttle (ENetPeer *, enet_uint32);
|
||||
extern void enet_peer_reset_queues (ENetPeer *);
|
||||
extern int enet_peer_has_outgoing_commands (ENetPeer *);
|
||||
extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *);
|
||||
extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16);
|
||||
extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32);
|
||||
extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16);
|
||||
extern void enet_peer_dispatch_incoming_unreliable_commands (ENetPeer *, ENetChannel *, ENetIncomingCommand *);
|
||||
extern void enet_peer_dispatch_incoming_reliable_commands (ENetPeer *, ENetChannel *, ENetIncomingCommand *);
|
||||
extern void enet_peer_on_connect (ENetPeer *);
|
||||
extern void enet_peer_on_disconnect (ENetPeer *);
|
||||
|
||||
ENET_API void * enet_range_coder_create (void);
|
||||
ENET_API void enet_range_coder_destroy (void *);
|
||||
ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t);
|
||||
ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t);
|
||||
|
||||
extern size_t enet_protocol_command_size (enet_uint8);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ENET_ENET_H__ */
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
@file list.h
|
||||
@brief ENet list management
|
||||
*/
|
||||
#ifndef __ENET_LIST_H__
|
||||
#define __ENET_LIST_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct _ENetListNode
|
||||
{
|
||||
struct _ENetListNode * next;
|
||||
struct _ENetListNode * previous;
|
||||
} ENetListNode;
|
||||
|
||||
typedef ENetListNode * ENetListIterator;
|
||||
|
||||
typedef struct _ENetList
|
||||
{
|
||||
ENetListNode sentinel;
|
||||
} ENetList;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
extern void enet_list_clear (ENetList *);
|
||||
|
||||
extern ENetListIterator enet_list_insert (ENetListIterator, void *);
|
||||
extern void * enet_list_remove (ENetListIterator);
|
||||
extern ENetListIterator enet_list_move (ENetListIterator, void *, void *);
|
||||
|
||||
extern size_t enet_list_size (ENetList *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define enet_list_begin(list) ((list) -> sentinel.next)
|
||||
#define enet_list_end(list) (& (list) -> sentinel)
|
||||
|
||||
#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list))
|
||||
|
||||
#define enet_list_next(iterator) ((iterator) -> next)
|
||||
#define enet_list_previous(iterator) ((iterator) -> previous)
|
||||
|
||||
#define enet_list_front(list) ((void *) (list) -> sentinel.next)
|
||||
#define enet_list_back(list) ((void *) (list) -> sentinel.previous)
|
||||
|
||||
#endif /* __ENET_LIST_H__ */
|
||||
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
/**
|
||||
@file protocol.h
|
||||
@brief ENet protocol
|
||||
*/
|
||||
#ifndef __ENET_PROTOCOL_H__
|
||||
#define __ENET_PROTOCOL_H__
|
||||
|
||||
#include "enet/types.h"
|
||||
|
||||
enum
|
||||
{
|
||||
ENET_PROTOCOL_MINIMUM_MTU = 576,
|
||||
ENET_PROTOCOL_MAXIMUM_MTU = 4096,
|
||||
ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32,
|
||||
ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096,
|
||||
ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536,
|
||||
ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1,
|
||||
ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255,
|
||||
ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF,
|
||||
ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024
|
||||
};
|
||||
|
||||
typedef enum _ENetProtocolCommand
|
||||
{
|
||||
ENET_PROTOCOL_COMMAND_NONE = 0,
|
||||
ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1,
|
||||
ENET_PROTOCOL_COMMAND_CONNECT = 2,
|
||||
ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3,
|
||||
ENET_PROTOCOL_COMMAND_DISCONNECT = 4,
|
||||
ENET_PROTOCOL_COMMAND_PING = 5,
|
||||
ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6,
|
||||
ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7,
|
||||
ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8,
|
||||
ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9,
|
||||
ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10,
|
||||
ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11,
|
||||
ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12,
|
||||
ENET_PROTOCOL_COMMAND_COUNT = 13,
|
||||
|
||||
ENET_PROTOCOL_COMMAND_MASK = 0x0F
|
||||
} ENetProtocolCommand;
|
||||
|
||||
typedef enum _ENetProtocolFlag
|
||||
{
|
||||
ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7),
|
||||
ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6),
|
||||
|
||||
ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14),
|
||||
ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15),
|
||||
ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME,
|
||||
|
||||
ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12),
|
||||
ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12
|
||||
} ENetProtocolFlag;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(push, 1)
|
||||
#define ENET_PACKED
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define ENET_PACKED __attribute__ ((packed))
|
||||
#else
|
||||
#define ENET_PACKED
|
||||
#endif
|
||||
|
||||
typedef struct _ENetProtocolHeader
|
||||
{
|
||||
enet_uint16 peerID;
|
||||
enet_uint16 sentTime;
|
||||
} ENET_PACKED ENetProtocolHeader;
|
||||
|
||||
typedef struct _ENetProtocolCommandHeader
|
||||
{
|
||||
enet_uint8 command;
|
||||
enet_uint8 channelID;
|
||||
enet_uint16 reliableSequenceNumber;
|
||||
} ENET_PACKED ENetProtocolCommandHeader;
|
||||
|
||||
typedef struct _ENetProtocolAcknowledge
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint16 receivedReliableSequenceNumber;
|
||||
enet_uint16 receivedSentTime;
|
||||
} ENET_PACKED ENetProtocolAcknowledge;
|
||||
|
||||
typedef struct _ENetProtocolConnect
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint16 outgoingPeerID;
|
||||
enet_uint8 incomingSessionID;
|
||||
enet_uint8 outgoingSessionID;
|
||||
enet_uint32 mtu;
|
||||
enet_uint32 windowSize;
|
||||
enet_uint32 channelCount;
|
||||
enet_uint32 incomingBandwidth;
|
||||
enet_uint32 outgoingBandwidth;
|
||||
enet_uint32 packetThrottleInterval;
|
||||
enet_uint32 packetThrottleAcceleration;
|
||||
enet_uint32 packetThrottleDeceleration;
|
||||
enet_uint32 connectID;
|
||||
enet_uint32 data;
|
||||
} ENET_PACKED ENetProtocolConnect;
|
||||
|
||||
typedef struct _ENetProtocolVerifyConnect
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint16 outgoingPeerID;
|
||||
enet_uint8 incomingSessionID;
|
||||
enet_uint8 outgoingSessionID;
|
||||
enet_uint32 mtu;
|
||||
enet_uint32 windowSize;
|
||||
enet_uint32 channelCount;
|
||||
enet_uint32 incomingBandwidth;
|
||||
enet_uint32 outgoingBandwidth;
|
||||
enet_uint32 packetThrottleInterval;
|
||||
enet_uint32 packetThrottleAcceleration;
|
||||
enet_uint32 packetThrottleDeceleration;
|
||||
enet_uint32 connectID;
|
||||
} ENET_PACKED ENetProtocolVerifyConnect;
|
||||
|
||||
typedef struct _ENetProtocolBandwidthLimit
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint32 incomingBandwidth;
|
||||
enet_uint32 outgoingBandwidth;
|
||||
} ENET_PACKED ENetProtocolBandwidthLimit;
|
||||
|
||||
typedef struct _ENetProtocolThrottleConfigure
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint32 packetThrottleInterval;
|
||||
enet_uint32 packetThrottleAcceleration;
|
||||
enet_uint32 packetThrottleDeceleration;
|
||||
} ENET_PACKED ENetProtocolThrottleConfigure;
|
||||
|
||||
typedef struct _ENetProtocolDisconnect
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint32 data;
|
||||
} ENET_PACKED ENetProtocolDisconnect;
|
||||
|
||||
typedef struct _ENetProtocolPing
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
} ENET_PACKED ENetProtocolPing;
|
||||
|
||||
typedef struct _ENetProtocolSendReliable
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint16 dataLength;
|
||||
} ENET_PACKED ENetProtocolSendReliable;
|
||||
|
||||
typedef struct _ENetProtocolSendUnreliable
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint16 unreliableSequenceNumber;
|
||||
enet_uint16 dataLength;
|
||||
} ENET_PACKED ENetProtocolSendUnreliable;
|
||||
|
||||
typedef struct _ENetProtocolSendUnsequenced
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint16 unsequencedGroup;
|
||||
enet_uint16 dataLength;
|
||||
} ENET_PACKED ENetProtocolSendUnsequenced;
|
||||
|
||||
typedef struct _ENetProtocolSendFragment
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
enet_uint16 startSequenceNumber;
|
||||
enet_uint16 dataLength;
|
||||
enet_uint32 fragmentCount;
|
||||
enet_uint32 fragmentNumber;
|
||||
enet_uint32 totalLength;
|
||||
enet_uint32 fragmentOffset;
|
||||
} ENET_PACKED ENetProtocolSendFragment;
|
||||
|
||||
typedef union _ENetProtocol
|
||||
{
|
||||
ENetProtocolCommandHeader header;
|
||||
ENetProtocolAcknowledge acknowledge;
|
||||
ENetProtocolConnect connect;
|
||||
ENetProtocolVerifyConnect verifyConnect;
|
||||
ENetProtocolDisconnect disconnect;
|
||||
ENetProtocolPing ping;
|
||||
ENetProtocolSendReliable sendReliable;
|
||||
ENetProtocolSendUnreliable sendUnreliable;
|
||||
ENetProtocolSendUnsequenced sendUnsequenced;
|
||||
ENetProtocolSendFragment sendFragment;
|
||||
ENetProtocolBandwidthLimit bandwidthLimit;
|
||||
ENetProtocolThrottleConfigure throttleConfigure;
|
||||
} ENET_PACKED ENetProtocol;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#endif /* __ENET_PROTOCOL_H__ */
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
@file time.h
|
||||
@brief ENet time constants and macros
|
||||
*/
|
||||
#ifndef __ENET_TIME_H__
|
||||
#define __ENET_TIME_H__
|
||||
|
||||
#define ENET_TIME_OVERFLOW 86400000
|
||||
|
||||
#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW)
|
||||
#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW)
|
||||
#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b))
|
||||
#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b))
|
||||
|
||||
#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b))
|
||||
|
||||
#endif /* __ENET_TIME_H__ */
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
@file types.h
|
||||
@brief type definitions for ENet
|
||||
*/
|
||||
#ifndef __ENET_TYPES_H__
|
||||
#define __ENET_TYPES_H__
|
||||
|
||||
typedef unsigned char enet_uint8; /**< unsigned 8-bit type */
|
||||
typedef unsigned short enet_uint16; /**< unsigned 16-bit type */
|
||||
typedef unsigned int enet_uint32; /**< unsigned 32-bit type */
|
||||
|
||||
#endif /* __ENET_TYPES_H__ */
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
@file unix.h
|
||||
@brief ENet Unix header
|
||||
*/
|
||||
#ifndef __ENET_UNIX_H__
|
||||
#define __ENET_UNIX_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef MSG_MAXIOVLEN
|
||||
#define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN
|
||||
#endif
|
||||
|
||||
typedef int ENetSocket;
|
||||
|
||||
#define ENET_SOCKET_NULL -1
|
||||
|
||||
#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */
|
||||
#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */
|
||||
|
||||
#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */
|
||||
#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void * data;
|
||||
size_t dataLength;
|
||||
} ENetBuffer;
|
||||
|
||||
#define ENET_CALLBACK
|
||||
|
||||
#define ENET_API extern
|
||||
|
||||
typedef fd_set ENetSocketSet;
|
||||
|
||||
#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset))
|
||||
#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset))
|
||||
#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset))
|
||||
#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset))
|
||||
|
||||
#endif /* __ENET_UNIX_H__ */
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
@file utility.h
|
||||
@brief ENet utility header
|
||||
*/
|
||||
#ifndef __ENET_UTILITY_H__
|
||||
#define __ENET_UTILITY_H__
|
||||
|
||||
#ifdef HAS_OFFSETOF
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define ENET_DIFFERENCE(x, y) ((x) < (y) ? (y) - (x) : (x) - (y))
|
||||
|
||||
#ifdef HAS_OFFSETOF
|
||||
#define ENET_OFFSETOF(str, field) (offsetof(str, field))
|
||||
#else
|
||||
#define ENET_OFFSETOF(str, field) ((size_t) & ((str *) 0) -> field)
|
||||
#endif
|
||||
|
||||
#endif /* __ENET_UTILITY_H__ */
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
@file win32.h
|
||||
@brief ENet Win32 header
|
||||
*/
|
||||
#ifndef __ENET_WIN32_H__
|
||||
#define __ENET_WIN32_H__
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifdef ENET_BUILDING_LIB
|
||||
#pragma warning (disable: 4267) // size_t to int conversion
|
||||
#pragma warning (disable: 4244) // 64bit to 32bit int
|
||||
#pragma warning (disable: 4018) // signed/unsigned mismatch
|
||||
#pragma warning (disable: 4146) // unary minus operator applied to unsigned type
|
||||
#ifndef _CRT_SECURE_NO_DEPRECATE
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#endif
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <winsock2.h>
|
||||
|
||||
typedef SOCKET ENetSocket;
|
||||
|
||||
#define ENET_SOCKET_NULL INVALID_SOCKET
|
||||
|
||||
#define ENET_HOST_TO_NET_16(value) (htons (value))
|
||||
#define ENET_HOST_TO_NET_32(value) (htonl (value))
|
||||
|
||||
#define ENET_NET_TO_HOST_16(value) (ntohs (value))
|
||||
#define ENET_NET_TO_HOST_32(value) (ntohl (value))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t dataLength;
|
||||
void * data;
|
||||
} ENetBuffer;
|
||||
|
||||
#define ENET_CALLBACK __cdecl
|
||||
|
||||
#ifdef ENET_DLL
|
||||
#ifdef ENET_BUILDING_LIB
|
||||
#define ENET_API __declspec( dllexport )
|
||||
#else
|
||||
#define ENET_API __declspec( dllimport )
|
||||
#endif /* ENET_BUILDING_LIB */
|
||||
#else /* !ENET_DLL */
|
||||
#define ENET_API extern
|
||||
#endif /* ENET_DLL */
|
||||
|
||||
typedef fd_set ENetSocketSet;
|
||||
|
||||
#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset))
|
||||
#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset))
|
||||
#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset))
|
||||
#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset))
|
||||
|
||||
#endif /* __ENET_WIN32_H__ */
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: @PACKAGE_NAME@
|
||||
Description: Low-latency UDP networking library supporting optional reliability
|
||||
Version: @PACKAGE_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -lenet
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
@file list.c
|
||||
@brief ENet linked list functions
|
||||
*/
|
||||
#define ENET_BUILDING_LIB 1
|
||||
#include "enet/enet.h"
|
||||
|
||||
/**
|
||||
@defgroup list ENet linked list utility functions
|
||||
@ingroup private
|
||||
@{
|
||||
*/
|
||||
void
|
||||
enet_list_clear (ENetList * list)
|
||||
{
|
||||
list -> sentinel.next = & list -> sentinel;
|
||||
list -> sentinel.previous = & list -> sentinel;
|
||||
}
|
||||
|
||||
ENetListIterator
|
||||
enet_list_insert (ENetListIterator position, void * data)
|
||||
{
|
||||
ENetListIterator result = (ENetListIterator) data;
|
||||
|
||||
result -> previous = position -> previous;
|
||||
result -> next = position;
|
||||
|
||||
result -> previous -> next = result;
|
||||
position -> previous = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void *
|
||||
enet_list_remove (ENetListIterator position)
|
||||
{
|
||||
position -> previous -> next = position -> next;
|
||||
position -> next -> previous = position -> previous;
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
ENetListIterator
|
||||
enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast)
|
||||
{
|
||||
ENetListIterator first = (ENetListIterator) dataFirst,
|
||||
last = (ENetListIterator) dataLast;
|
||||
|
||||
first -> previous -> next = last -> next;
|
||||
last -> next -> previous = first -> previous;
|
||||
|
||||
first -> previous = position -> previous;
|
||||
last -> next = position;
|
||||
|
||||
first -> previous -> next = first;
|
||||
position -> previous = last;
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
size_t
|
||||
enet_list_size (ENetList * list)
|
||||
{
|
||||
size_t size = 0;
|
||||
ENetListIterator position;
|
||||
|
||||
for (position = enet_list_begin (list);
|
||||
position != enet_list_end (list);
|
||||
position = enet_list_next (position))
|
||||
++ size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
/**
|
||||
@file packet.c
|
||||
@brief ENet packet management functions
|
||||
*/
|
||||
#include <string.h>
|
||||
#define ENET_BUILDING_LIB 1
|
||||
#include "enet/enet.h"
|
||||
|
||||
/** @defgroup Packet ENet packet functions
|
||||
@{
|
||||
*/
|
||||
|
||||
/** Creates a packet that may be sent to a peer.
|
||||
@param data initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL.
|
||||
@param dataLength size of the data allocated for this packet
|
||||
@param flags flags for this packet as described for the ENetPacket structure.
|
||||
@returns the packet on success, NULL on failure
|
||||
*/
|
||||
ENetPacket *
|
||||
enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags)
|
||||
{
|
||||
ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket));
|
||||
if (packet == NULL)
|
||||
return NULL;
|
||||
|
||||
if (flags & ENET_PACKET_FLAG_NO_ALLOCATE)
|
||||
packet -> data = (enet_uint8 *) data;
|
||||
else
|
||||
if (dataLength <= 0)
|
||||
packet -> data = NULL;
|
||||
else
|
||||
{
|
||||
packet -> data = (enet_uint8 *) enet_malloc (dataLength);
|
||||
if (packet -> data == NULL)
|
||||
{
|
||||
enet_free (packet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (data != NULL)
|
||||
memcpy (packet -> data, data, dataLength);
|
||||
}
|
||||
|
||||
packet -> referenceCount = 0;
|
||||
packet -> flags = flags;
|
||||
packet -> dataLength = dataLength;
|
||||
packet -> freeCallback = NULL;
|
||||
packet -> userData = NULL;
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/** Destroys the packet and deallocates its data.
|
||||
@param packet packet to be destroyed
|
||||
*/
|
||||
void
|
||||
enet_packet_destroy (ENetPacket * packet)
|
||||
{
|
||||
if (packet == NULL)
|
||||
return;
|
||||
|
||||
if (packet -> freeCallback != NULL)
|
||||
(* packet -> freeCallback) (packet);
|
||||
if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) &&
|
||||
packet -> data != NULL)
|
||||
enet_free (packet -> data);
|
||||
enet_free (packet);
|
||||
}
|
||||
|
||||
/** Attempts to resize the data in the packet to length specified in the
|
||||
dataLength parameter
|
||||
@param packet packet to resize
|
||||
@param dataLength new size for the packet data
|
||||
@returns 0 on success, < 0 on failure
|
||||
*/
|
||||
int
|
||||
enet_packet_resize (ENetPacket * packet, size_t dataLength)
|
||||
{
|
||||
enet_uint8 * newData;
|
||||
|
||||
if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE))
|
||||
{
|
||||
packet -> dataLength = dataLength;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
newData = (enet_uint8 *) enet_malloc (dataLength);
|
||||
if (newData == NULL)
|
||||
return -1;
|
||||
|
||||
if (packet -> data != NULL)
|
||||
{
|
||||
if (packet -> dataLength > 0)
|
||||
memcpy (newData, packet -> data, packet -> dataLength);
|
||||
|
||||
enet_free (packet -> data);
|
||||
}
|
||||
|
||||
packet -> data = newData;
|
||||
packet -> dataLength = dataLength;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const enet_uint32 crcTable [256] =
|
||||
{
|
||||
0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x5005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
enet_uint32
|
||||
enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
|
||||
{
|
||||
enet_uint32 crc = 0xFFFFFFFF;
|
||||
|
||||
while (bufferCount -- > 0)
|
||||
{
|
||||
const enet_uint8 * data = (const enet_uint8 *) buffers -> data,
|
||||
* dataEnd = & data [buffers -> dataLength];
|
||||
|
||||
while (data < dataEnd)
|
||||
{
|
||||
crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];
|
||||
}
|
||||
|
||||
++ buffers;
|
||||
}
|
||||
|
||||
return ENET_HOST_TO_NET_32 (~ crc);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,59 @@
|
|||
solution "enet"
|
||||
configurations { "Debug", "Release" }
|
||||
platforms { "x32", "x64" }
|
||||
|
||||
project "enet_static"
|
||||
kind "StaticLib"
|
||||
language "C"
|
||||
|
||||
files { "*.c" }
|
||||
|
||||
includedirs { "include/" }
|
||||
|
||||
configuration "Debug"
|
||||
targetsuffix "d"
|
||||
|
||||
defines({ "DEBUG" })
|
||||
|
||||
flags { "Symbols" }
|
||||
|
||||
configuration "Release"
|
||||
defines({ "NDEBUG" })
|
||||
|
||||
flags { "Optimize" }
|
||||
|
||||
configuration { "Debug", "x64" }
|
||||
targetsuffix "64d"
|
||||
|
||||
configuration { "Release", "x64" }
|
||||
targetsuffix "64"
|
||||
|
||||
project "enet"
|
||||
kind "SharedLib"
|
||||
language "C"
|
||||
|
||||
files { "*.c" }
|
||||
|
||||
includedirs { "include/" }
|
||||
|
||||
defines({"ENET_DLL=1" })
|
||||
|
||||
configuration "Debug"
|
||||
targetsuffix "d"
|
||||
|
||||
defines({ "DEBUG" })
|
||||
|
||||
flags { "Symbols" }
|
||||
|
||||
configuration "Release"
|
||||
defines({ "NDEBUG" })
|
||||
|
||||
flags { "Optimize" }
|
||||
|
||||
configuration { "Debug", "x64" }
|
||||
targetsuffix "64d"
|
||||
|
||||
configuration { "Release", "x64" }
|
||||
targetsuffix "64"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,630 @@
|
|||
/**
|
||||
@file unix.c
|
||||
@brief ENet Unix system specific functions
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#define ENET_BUILDING_LIB 1
|
||||
#include "enet/enet.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef HAS_POLL
|
||||
#undef HAS_POLL
|
||||
#endif
|
||||
#ifndef HAS_FCNTL
|
||||
#define HAS_FCNTL 1
|
||||
#endif
|
||||
#ifndef HAS_INET_PTON
|
||||
#define HAS_INET_PTON 1
|
||||
#endif
|
||||
#ifndef HAS_INET_NTOP
|
||||
#define HAS_INET_NTOP 1
|
||||
#endif
|
||||
#ifndef HAS_MSGHDR_FLAGS
|
||||
#define HAS_MSGHDR_FLAGS 1
|
||||
#endif
|
||||
#ifndef HAS_SOCKLEN_T
|
||||
#define HAS_SOCKLEN_T 1
|
||||
#endif
|
||||
#ifndef HAS_GETADDRINFO
|
||||
#define HAS_GETADDRINFO 1
|
||||
#endif
|
||||
#ifndef HAS_GETNAMEINFO
|
||||
#define HAS_GETNAMEINFO 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAS_FCNTL
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAS_POLL
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAS_SOCKLEN_T) && !defined(__socklen_t_defined)
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
static enet_uint32 timeBase = 0;
|
||||
|
||||
int
|
||||
enet_initialize (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
enet_deinitialize (void)
|
||||
{
|
||||
}
|
||||
|
||||
enet_uint32
|
||||
enet_host_random_seed (void)
|
||||
{
|
||||
return (enet_uint32) time (NULL);
|
||||
}
|
||||
|
||||
enet_uint32
|
||||
enet_time_get (void)
|
||||
{
|
||||
struct timeval timeVal;
|
||||
|
||||
gettimeofday (& timeVal, NULL);
|
||||
|
||||
return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase;
|
||||
}
|
||||
|
||||
void
|
||||
enet_time_set (enet_uint32 newTimeBase)
|
||||
{
|
||||
struct timeval timeVal;
|
||||
|
||||
gettimeofday (& timeVal, NULL);
|
||||
|
||||
timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase;
|
||||
}
|
||||
|
||||
int
|
||||
enet_address_set_host_ip (ENetAddress * address, const char * name)
|
||||
{
|
||||
#ifdef HAS_INET_PTON
|
||||
if (! inet_pton (AF_INET, name, & address -> host))
|
||||
#else
|
||||
if (! inet_aton (name, (struct in_addr *) & address -> host))
|
||||
#endif
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_address_set_host (ENetAddress * address, const char * name)
|
||||
{
|
||||
#ifdef HAS_GETADDRINFO
|
||||
struct addrinfo hints, * resultList = NULL, * result = NULL;
|
||||
|
||||
memset (& hints, 0, sizeof (hints));
|
||||
hints.ai_family = AF_INET;
|
||||
|
||||
if (getaddrinfo (name, NULL, NULL, & resultList) != 0)
|
||||
return -1;
|
||||
|
||||
for (result = resultList; result != NULL; result = result -> ai_next)
|
||||
{
|
||||
if (result -> ai_family == AF_INET && result -> ai_addr != NULL && result -> ai_addrlen >= sizeof (struct sockaddr_in))
|
||||
{
|
||||
struct sockaddr_in * sin = (struct sockaddr_in *) result -> ai_addr;
|
||||
|
||||
address -> host = sin -> sin_addr.s_addr;
|
||||
|
||||
freeaddrinfo (resultList);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (resultList != NULL)
|
||||
freeaddrinfo (resultList);
|
||||
#else
|
||||
struct hostent * hostEntry = NULL;
|
||||
#ifdef HAS_GETHOSTBYNAME_R
|
||||
struct hostent hostData;
|
||||
char buffer [2048];
|
||||
int errnum;
|
||||
|
||||
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__GNU__)
|
||||
gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
|
||||
#else
|
||||
hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum);
|
||||
#endif
|
||||
#else
|
||||
hostEntry = gethostbyname (name);
|
||||
#endif
|
||||
|
||||
if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET)
|
||||
{
|
||||
address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return enet_address_set_host_ip (address, name);
|
||||
}
|
||||
|
||||
int
|
||||
enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
|
||||
{
|
||||
#ifdef HAS_INET_NTOP
|
||||
if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL)
|
||||
#else
|
||||
char * addr = inet_ntoa (* (struct in_addr *) & address -> host);
|
||||
if (addr != NULL)
|
||||
{
|
||||
size_t addrLen = strlen(addr);
|
||||
if (addrLen >= nameLength)
|
||||
return -1;
|
||||
memcpy (name, addr, addrLen + 1);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
|
||||
{
|
||||
#ifdef HAS_GETNAMEINFO
|
||||
struct sockaddr_in sin;
|
||||
int err;
|
||||
|
||||
memset (& sin, 0, sizeof (struct sockaddr_in));
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
|
||||
sin.sin_addr.s_addr = address -> host;
|
||||
|
||||
err = getnameinfo ((struct sockaddr *) & sin, sizeof (sin), name, nameLength, NULL, 0, NI_NAMEREQD);
|
||||
if (! err)
|
||||
{
|
||||
if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
if (err != EAI_NONAME)
|
||||
return -1;
|
||||
#else
|
||||
struct in_addr in;
|
||||
struct hostent * hostEntry = NULL;
|
||||
#ifdef HAS_GETHOSTBYADDR_R
|
||||
struct hostent hostData;
|
||||
char buffer [2048];
|
||||
int errnum;
|
||||
|
||||
in.s_addr = address -> host;
|
||||
|
||||
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__GNU__)
|
||||
gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
|
||||
#else
|
||||
hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum);
|
||||
#endif
|
||||
#else
|
||||
in.s_addr = address -> host;
|
||||
|
||||
hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
|
||||
#endif
|
||||
|
||||
if (hostEntry != NULL)
|
||||
{
|
||||
size_t hostLen = strlen (hostEntry -> h_name);
|
||||
if (hostLen >= nameLength)
|
||||
return -1;
|
||||
memcpy (name, hostEntry -> h_name, hostLen + 1);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return enet_address_get_host_ip (address, name, nameLength);
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_bind (ENetSocket socket, const ENetAddress * address)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
|
||||
memset (& sin, 0, sizeof (struct sockaddr_in));
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
|
||||
sin.sin_addr.s_addr = address -> host;
|
||||
}
|
||||
else
|
||||
{
|
||||
sin.sin_port = 0;
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
return bind (socket,
|
||||
(struct sockaddr *) & sin,
|
||||
sizeof (struct sockaddr_in));
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_get_address (ENetSocket socket, ENetAddress * address)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
socklen_t sinLength = sizeof (struct sockaddr_in);
|
||||
|
||||
if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
|
||||
return -1;
|
||||
|
||||
address -> host = (enet_uint32) sin.sin_addr.s_addr;
|
||||
address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_listen (ENetSocket socket, int backlog)
|
||||
{
|
||||
return listen (socket, backlog < 0 ? SOMAXCONN : backlog);
|
||||
}
|
||||
|
||||
ENetSocket
|
||||
enet_socket_create (ENetSocketType type)
|
||||
{
|
||||
return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value)
|
||||
{
|
||||
int result = -1;
|
||||
switch (option)
|
||||
{
|
||||
case ENET_SOCKOPT_NONBLOCK:
|
||||
#ifdef HAS_FCNTL
|
||||
result = fcntl (socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl (socket, F_GETFL) & ~O_NONBLOCK));
|
||||
#else
|
||||
result = ioctl (socket, FIONBIO, & value);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_BROADCAST:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_REUSEADDR:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_RCVBUF:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_SNDBUF:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_RCVTIMEO:
|
||||
{
|
||||
struct timeval timeVal;
|
||||
timeVal.tv_sec = value / 1000;
|
||||
timeVal.tv_usec = (value % 1000) * 1000;
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & timeVal, sizeof (struct timeval));
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_SOCKOPT_SNDTIMEO:
|
||||
{
|
||||
struct timeval timeVal;
|
||||
timeVal.tv_sec = value / 1000;
|
||||
timeVal.tv_usec = (value % 1000) * 1000;
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & timeVal, sizeof (struct timeval));
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_SOCKOPT_NODELAY:
|
||||
result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_TTL:
|
||||
result = setsockopt (socket, IPPROTO_IP, IP_TTL, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result == -1 ? -1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value)
|
||||
{
|
||||
int result = -1;
|
||||
socklen_t len;
|
||||
switch (option)
|
||||
{
|
||||
case ENET_SOCKOPT_ERROR:
|
||||
len = sizeof (int);
|
||||
result = getsockopt (socket, SOL_SOCKET, SO_ERROR, value, & len);
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_TTL:
|
||||
len = sizeof (int);
|
||||
result = getsockopt (socket, IPPROTO_IP, IP_TTL, (char *) value, & len);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result == -1 ? -1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_connect (ENetSocket socket, const ENetAddress * address)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
int result;
|
||||
|
||||
memset (& sin, 0, sizeof (struct sockaddr_in));
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
|
||||
sin.sin_addr.s_addr = address -> host;
|
||||
|
||||
result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
|
||||
if (result == -1 && errno == EINPROGRESS)
|
||||
return 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ENetSocket
|
||||
enet_socket_accept (ENetSocket socket, ENetAddress * address)
|
||||
{
|
||||
int result;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t sinLength = sizeof (struct sockaddr_in);
|
||||
|
||||
result = accept (socket,
|
||||
address != NULL ? (struct sockaddr *) & sin : NULL,
|
||||
address != NULL ? & sinLength : NULL);
|
||||
|
||||
if (result == -1)
|
||||
return ENET_SOCKET_NULL;
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
address -> host = (enet_uint32) sin.sin_addr.s_addr;
|
||||
address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how)
|
||||
{
|
||||
return shutdown (socket, (int) how);
|
||||
}
|
||||
|
||||
void
|
||||
enet_socket_destroy (ENetSocket socket)
|
||||
{
|
||||
if (socket != -1)
|
||||
close (socket);
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_send (ENetSocket socket,
|
||||
const ENetAddress * address,
|
||||
const ENetBuffer * buffers,
|
||||
size_t bufferCount)
|
||||
{
|
||||
struct msghdr msgHdr;
|
||||
struct sockaddr_in sin;
|
||||
int sentLength;
|
||||
|
||||
memset (& msgHdr, 0, sizeof (struct msghdr));
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
memset (& sin, 0, sizeof (struct sockaddr_in));
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
|
||||
sin.sin_addr.s_addr = address -> host;
|
||||
|
||||
msgHdr.msg_name = & sin;
|
||||
msgHdr.msg_namelen = sizeof (struct sockaddr_in);
|
||||
}
|
||||
|
||||
msgHdr.msg_iov = (struct iovec *) buffers;
|
||||
msgHdr.msg_iovlen = bufferCount;
|
||||
|
||||
sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL);
|
||||
|
||||
if (sentLength == -1)
|
||||
{
|
||||
if (errno == EWOULDBLOCK)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sentLength;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_receive (ENetSocket socket,
|
||||
ENetAddress * address,
|
||||
ENetBuffer * buffers,
|
||||
size_t bufferCount)
|
||||
{
|
||||
struct msghdr msgHdr;
|
||||
struct sockaddr_in sin;
|
||||
int recvLength;
|
||||
|
||||
memset (& msgHdr, 0, sizeof (struct msghdr));
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
msgHdr.msg_name = & sin;
|
||||
msgHdr.msg_namelen = sizeof (struct sockaddr_in);
|
||||
}
|
||||
|
||||
msgHdr.msg_iov = (struct iovec *) buffers;
|
||||
msgHdr.msg_iovlen = bufferCount;
|
||||
|
||||
recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL);
|
||||
|
||||
if (recvLength == -1)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
case EWOULDBLOCK:
|
||||
return 0;
|
||||
case EINTR:
|
||||
case EMSGSIZE:
|
||||
return -2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_MSGHDR_FLAGS
|
||||
if (msgHdr.msg_flags & MSG_TRUNC)
|
||||
return -2;
|
||||
#endif
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
address -> host = (enet_uint32) sin.sin_addr.s_addr;
|
||||
address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
|
||||
}
|
||||
|
||||
return recvLength;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
|
||||
{
|
||||
struct timeval timeVal;
|
||||
|
||||
timeVal.tv_sec = timeout / 1000;
|
||||
timeVal.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal);
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
|
||||
{
|
||||
#ifdef HAS_POLL
|
||||
struct pollfd pollSocket;
|
||||
int pollCount;
|
||||
|
||||
pollSocket.fd = socket;
|
||||
pollSocket.events = 0;
|
||||
|
||||
if (* condition & ENET_SOCKET_WAIT_SEND)
|
||||
pollSocket.events |= POLLOUT;
|
||||
|
||||
if (* condition & ENET_SOCKET_WAIT_RECEIVE)
|
||||
pollSocket.events |= POLLIN;
|
||||
|
||||
pollCount = poll (& pollSocket, 1, timeout);
|
||||
|
||||
if (pollCount < 0)
|
||||
{
|
||||
if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT)
|
||||
{
|
||||
* condition = ENET_SOCKET_WAIT_INTERRUPT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
* condition = ENET_SOCKET_WAIT_NONE;
|
||||
|
||||
if (pollCount == 0)
|
||||
return 0;
|
||||
|
||||
if (pollSocket.revents & POLLOUT)
|
||||
* condition |= ENET_SOCKET_WAIT_SEND;
|
||||
|
||||
if (pollSocket.revents & POLLIN)
|
||||
* condition |= ENET_SOCKET_WAIT_RECEIVE;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
fd_set readSet, writeSet;
|
||||
struct timeval timeVal;
|
||||
int selectCount;
|
||||
|
||||
timeVal.tv_sec = timeout / 1000;
|
||||
timeVal.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
FD_ZERO (& readSet);
|
||||
FD_ZERO (& writeSet);
|
||||
|
||||
if (* condition & ENET_SOCKET_WAIT_SEND)
|
||||
FD_SET (socket, & writeSet);
|
||||
|
||||
if (* condition & ENET_SOCKET_WAIT_RECEIVE)
|
||||
FD_SET (socket, & readSet);
|
||||
|
||||
selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
|
||||
|
||||
if (selectCount < 0)
|
||||
{
|
||||
if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT)
|
||||
{
|
||||
* condition = ENET_SOCKET_WAIT_INTERRUPT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
* condition = ENET_SOCKET_WAIT_NONE;
|
||||
|
||||
if (selectCount == 0)
|
||||
return 0;
|
||||
|
||||
if (FD_ISSET (socket, & writeSet))
|
||||
* condition |= ENET_SOCKET_WAIT_SEND;
|
||||
|
||||
if (FD_ISSET (socket, & readSet))
|
||||
* condition |= ENET_SOCKET_WAIT_RECEIVE;
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,455 @@
|
|||
/**
|
||||
@file win32.c
|
||||
@brief ENet Win32 system specific functions
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
|
||||
#define ENET_BUILDING_LIB 1
|
||||
#include "enet/enet.h"
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
static enet_uint32 timeBase = 0;
|
||||
|
||||
int
|
||||
enet_initialize (void)
|
||||
{
|
||||
WORD versionRequested = MAKEWORD (1, 1);
|
||||
WSADATA wsaData;
|
||||
|
||||
if (WSAStartup (versionRequested, & wsaData))
|
||||
return -1;
|
||||
|
||||
if (LOBYTE (wsaData.wVersion) != 1||
|
||||
HIBYTE (wsaData.wVersion) != 1)
|
||||
{
|
||||
WSACleanup ();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
timeBeginPeriod (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
enet_deinitialize (void)
|
||||
{
|
||||
timeEndPeriod (1);
|
||||
|
||||
WSACleanup ();
|
||||
}
|
||||
|
||||
enet_uint32
|
||||
enet_host_random_seed (void)
|
||||
{
|
||||
return (enet_uint32) timeGetTime ();
|
||||
}
|
||||
|
||||
enet_uint32
|
||||
enet_time_get (void)
|
||||
{
|
||||
return (enet_uint32) timeGetTime () - timeBase;
|
||||
}
|
||||
|
||||
void
|
||||
enet_time_set (enet_uint32 newTimeBase)
|
||||
{
|
||||
timeBase = (enet_uint32) timeGetTime () - newTimeBase;
|
||||
}
|
||||
|
||||
int
|
||||
enet_address_set_host_ip (ENetAddress * address, const char * name)
|
||||
{
|
||||
enet_uint8 vals [4] = { 0, 0, 0, 0 };
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; ++ i)
|
||||
{
|
||||
const char * next = name + 1;
|
||||
if (* name != '0')
|
||||
{
|
||||
long val = strtol (name, (char **) & next, 10);
|
||||
if (val < 0 || val > 255 || next == name || next - name > 3)
|
||||
return -1;
|
||||
vals [i] = (enet_uint8) val;
|
||||
}
|
||||
|
||||
if (* next != (i < 3 ? '.' : '\0'))
|
||||
return -1;
|
||||
name = next + 1;
|
||||
}
|
||||
|
||||
memcpy (& address -> host, vals, sizeof (enet_uint32));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_address_set_host (ENetAddress * address, const char * name)
|
||||
{
|
||||
struct hostent * hostEntry;
|
||||
|
||||
hostEntry = gethostbyname (name);
|
||||
if (hostEntry == NULL ||
|
||||
hostEntry -> h_addrtype != AF_INET)
|
||||
return enet_address_set_host_ip (address, name);
|
||||
|
||||
address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
|
||||
{
|
||||
char * addr = inet_ntoa (* (struct in_addr *) & address -> host);
|
||||
if (addr == NULL)
|
||||
return -1;
|
||||
else
|
||||
{
|
||||
size_t addrLen = strlen(addr);
|
||||
if (addrLen >= nameLength)
|
||||
return -1;
|
||||
memcpy (name, addr, addrLen + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
|
||||
{
|
||||
struct in_addr in;
|
||||
struct hostent * hostEntry;
|
||||
|
||||
in.s_addr = address -> host;
|
||||
|
||||
hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
|
||||
if (hostEntry == NULL)
|
||||
return enet_address_get_host_ip (address, name, nameLength);
|
||||
else
|
||||
{
|
||||
size_t hostLen = strlen (hostEntry -> h_name);
|
||||
if (hostLen >= nameLength)
|
||||
return -1;
|
||||
memcpy (name, hostEntry -> h_name, hostLen + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_bind (ENetSocket socket, const ENetAddress * address)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
|
||||
memset (& sin, 0, sizeof (struct sockaddr_in));
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
|
||||
sin.sin_addr.s_addr = address -> host;
|
||||
}
|
||||
else
|
||||
{
|
||||
sin.sin_port = 0;
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
return bind (socket,
|
||||
(struct sockaddr *) & sin,
|
||||
sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_get_address (ENetSocket socket, ENetAddress * address)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
int sinLength = sizeof (struct sockaddr_in);
|
||||
|
||||
if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
|
||||
return -1;
|
||||
|
||||
address -> host = (enet_uint32) sin.sin_addr.s_addr;
|
||||
address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_listen (ENetSocket socket, int backlog)
|
||||
{
|
||||
return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0;
|
||||
}
|
||||
|
||||
ENetSocket
|
||||
enet_socket_create (ENetSocketType type)
|
||||
{
|
||||
return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value)
|
||||
{
|
||||
int result = SOCKET_ERROR;
|
||||
switch (option)
|
||||
{
|
||||
case ENET_SOCKOPT_NONBLOCK:
|
||||
{
|
||||
u_long nonBlocking = (u_long) value;
|
||||
result = ioctlsocket (socket, FIONBIO, & nonBlocking);
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_SOCKOPT_BROADCAST:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_REUSEADDR:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_RCVBUF:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_SNDBUF:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_RCVTIMEO:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_SNDTIMEO:
|
||||
result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_NODELAY:
|
||||
result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_TTL:
|
||||
result = setsockopt (socket, IPPROTO_IP, IP_TTL, (char *) & value, sizeof (int));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result == SOCKET_ERROR ? -1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value)
|
||||
{
|
||||
int result = SOCKET_ERROR, len;
|
||||
switch (option)
|
||||
{
|
||||
case ENET_SOCKOPT_ERROR:
|
||||
len = sizeof(int);
|
||||
result = getsockopt (socket, SOL_SOCKET, SO_ERROR, (char *) value, & len);
|
||||
break;
|
||||
|
||||
case ENET_SOCKOPT_TTL:
|
||||
len = sizeof(int);
|
||||
result = getsockopt (socket, IPPROTO_IP, IP_TTL, (char *) value, & len);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result == SOCKET_ERROR ? -1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_connect (ENetSocket socket, const ENetAddress * address)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
int result;
|
||||
|
||||
memset (& sin, 0, sizeof (struct sockaddr_in));
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
|
||||
sin.sin_addr.s_addr = address -> host;
|
||||
|
||||
result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
|
||||
if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ENetSocket
|
||||
enet_socket_accept (ENetSocket socket, ENetAddress * address)
|
||||
{
|
||||
SOCKET result;
|
||||
struct sockaddr_in sin;
|
||||
int sinLength = sizeof (struct sockaddr_in);
|
||||
|
||||
result = accept (socket,
|
||||
address != NULL ? (struct sockaddr *) & sin : NULL,
|
||||
address != NULL ? & sinLength : NULL);
|
||||
|
||||
if (result == INVALID_SOCKET)
|
||||
return ENET_SOCKET_NULL;
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
address -> host = (enet_uint32) sin.sin_addr.s_addr;
|
||||
address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how)
|
||||
{
|
||||
return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0;
|
||||
}
|
||||
|
||||
void
|
||||
enet_socket_destroy (ENetSocket socket)
|
||||
{
|
||||
if (socket != INVALID_SOCKET)
|
||||
closesocket (socket);
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_send (ENetSocket socket,
|
||||
const ENetAddress * address,
|
||||
const ENetBuffer * buffers,
|
||||
size_t bufferCount)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
DWORD sentLength = 0;
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
memset (& sin, 0, sizeof (struct sockaddr_in));
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
|
||||
sin.sin_addr.s_addr = address -> host;
|
||||
}
|
||||
|
||||
if (WSASendTo (socket,
|
||||
(LPWSABUF) buffers,
|
||||
(DWORD) bufferCount,
|
||||
& sentLength,
|
||||
0,
|
||||
address != NULL ? (struct sockaddr *) & sin : NULL,
|
||||
address != NULL ? sizeof (struct sockaddr_in) : 0,
|
||||
NULL,
|
||||
NULL) == SOCKET_ERROR)
|
||||
{
|
||||
if (WSAGetLastError () == WSAEWOULDBLOCK)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int) sentLength;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_receive (ENetSocket socket,
|
||||
ENetAddress * address,
|
||||
ENetBuffer * buffers,
|
||||
size_t bufferCount)
|
||||
{
|
||||
INT sinLength = sizeof (struct sockaddr_in);
|
||||
DWORD flags = 0,
|
||||
recvLength = 0;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
if (WSARecvFrom (socket,
|
||||
(LPWSABUF) buffers,
|
||||
(DWORD) bufferCount,
|
||||
& recvLength,
|
||||
& flags,
|
||||
address != NULL ? (struct sockaddr *) & sin : NULL,
|
||||
address != NULL ? & sinLength : NULL,
|
||||
NULL,
|
||||
NULL) == SOCKET_ERROR)
|
||||
{
|
||||
switch (WSAGetLastError ())
|
||||
{
|
||||
case WSAEWOULDBLOCK:
|
||||
case WSAECONNRESET:
|
||||
return 0;
|
||||
case WSAEINTR:
|
||||
case WSAEMSGSIZE:
|
||||
return -2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & MSG_PARTIAL)
|
||||
return -2;
|
||||
|
||||
if (address != NULL)
|
||||
{
|
||||
address -> host = (enet_uint32) sin.sin_addr.s_addr;
|
||||
address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
|
||||
}
|
||||
|
||||
return (int) recvLength;
|
||||
}
|
||||
|
||||
int
|
||||
enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
|
||||
{
|
||||
struct timeval timeVal;
|
||||
|
||||
timeVal.tv_sec = timeout / 1000;
|
||||
timeVal.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal);
|
||||
}
|
||||
|
||||
int
|
||||
enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
|
||||
{
|
||||
fd_set readSet, writeSet;
|
||||
struct timeval timeVal;
|
||||
int selectCount;
|
||||
|
||||
timeVal.tv_sec = timeout / 1000;
|
||||
timeVal.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
FD_ZERO (& readSet);
|
||||
FD_ZERO (& writeSet);
|
||||
|
||||
if (* condition & ENET_SOCKET_WAIT_SEND)
|
||||
FD_SET (socket, & writeSet);
|
||||
|
||||
if (* condition & ENET_SOCKET_WAIT_RECEIVE)
|
||||
FD_SET (socket, & readSet);
|
||||
|
||||
selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
|
||||
|
||||
if (selectCount < 0)
|
||||
return -1;
|
||||
|
||||
* condition = ENET_SOCKET_WAIT_NONE;
|
||||
|
||||
if (selectCount == 0)
|
||||
return 0;
|
||||
|
||||
if (FD_ISSET (socket, & writeSet))
|
||||
* condition |= ENET_SOCKET_WAIT_SEND;
|
||||
|
||||
if (FD_ISSET (socket, & readSet))
|
||||
* condition |= ENET_SOCKET_WAIT_RECEIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Generated Zig bindings for ENet
|
||||
// This file was automatically generated from ENet headers
|
||||
|
||||
const std = @import("std");
|
||||
pub const c = @cImport({
|
||||
@cInclude("enet/enet.h");
|
||||
});
|
||||
|
||||
pub fn initialize() !void {
|
||||
if (c.enet_initialize() != 0) {
|
||||
return error.ENetInitFailed;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinitialize() void {
|
||||
c.enet_deinitialize();
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
const std = @import("std");
|
||||
const enet = @import("enet");
|
||||
|
||||
const SERVER_HOST = "127.0.0.1";
|
||||
const SERVER_PORT = 7777;
|
||||
const CHANNEL_COUNT = 2;
|
||||
const CONNECT_TIMEOUT = 5000; // 5 seconds
|
||||
|
||||
pub fn main() !void {
|
||||
std.debug.print("Starting ENet test client, connecting to {s}:{}\n", .{ SERVER_HOST, SERVER_PORT });
|
||||
|
||||
// Initialize ENet
|
||||
try enet.initialize();
|
||||
defer enet.deinitialize();
|
||||
|
||||
// Create client host
|
||||
const host = enet.c.enet_host_create(null, 1, CHANNEL_COUNT, 0, 0);
|
||||
if (host == null) {
|
||||
std.debug.print("Failed to create ENet client host\n", .{});
|
||||
return error.HostCreationFailed;
|
||||
}
|
||||
defer enet.c.enet_host_destroy(host);
|
||||
|
||||
// Set up server address
|
||||
var address = enet.c.ENetAddress{
|
||||
.host = 0,
|
||||
.port = SERVER_PORT,
|
||||
};
|
||||
|
||||
// Resolve server hostname
|
||||
if (enet.c.enet_address_set_host(&address, SERVER_HOST) != 0) {
|
||||
std.debug.print("Failed to resolve server address: {s}\n", .{SERVER_HOST});
|
||||
return error.AddressResolutionFailed;
|
||||
}
|
||||
|
||||
// Connect to server
|
||||
const peer = enet.c.enet_host_connect(host, &address, CHANNEL_COUNT, 0);
|
||||
if (peer == null) {
|
||||
std.debug.print("Failed to create connection to server\n", .{});
|
||||
return error.ConnectionFailed;
|
||||
}
|
||||
|
||||
var event: enet.c.ENetEvent = undefined;
|
||||
var connected = false;
|
||||
|
||||
// Wait for connection to complete
|
||||
std.debug.print("Connecting to server...\n", .{});
|
||||
if (enet.c.enet_host_service(host, &event, CONNECT_TIMEOUT) > 0 and event.type == enet.c.ENET_EVENT_TYPE_CONNECT) {
|
||||
std.debug.print("Connected to server successfully!\n", .{});
|
||||
connected = true;
|
||||
} else {
|
||||
std.debug.print("Failed to connect to server within timeout\n", .{});
|
||||
enet.c.enet_peer_reset(peer);
|
||||
return error.ConnectionTimeout;
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
// Send test messages
|
||||
const test_messages = [_][]const u8{
|
||||
"Hello from test client!",
|
||||
"This is message 2",
|
||||
"Testing ENet loopback",
|
||||
"Final message",
|
||||
"quit", // This will trigger server shutdown
|
||||
};
|
||||
|
||||
for (test_messages, 0..) |message, i| {
|
||||
std.debug.print("Sending message {}: '{s}'\n", .{ i + 1, message });
|
||||
|
||||
// Create and send packet
|
||||
const packet = enet.c.enet_packet_create(message.ptr, message.len, enet.c.ENET_PACKET_FLAG_RELIABLE);
|
||||
_ = enet.c.enet_peer_send(peer, 0, packet);
|
||||
|
||||
// Flush the send queue
|
||||
enet.c.enet_host_flush(host);
|
||||
|
||||
// Wait for response
|
||||
var response_received = false;
|
||||
var attempts: u32 = 0;
|
||||
const max_attempts = 10;
|
||||
|
||||
while (!response_received and attempts < max_attempts) {
|
||||
const result = enet.c.enet_host_service(host, &event, 500); // 500ms timeout per attempt
|
||||
attempts += 1;
|
||||
|
||||
if (result > 0) {
|
||||
switch (event.type) {
|
||||
enet.c.ENET_EVENT_TYPE_RECEIVE => {
|
||||
const data = @as([*]u8, @ptrCast(event.packet.*.data))[0..event.packet.*.dataLength];
|
||||
std.debug.print("Received response: '{s}'\n", .{data});
|
||||
enet.c.enet_packet_destroy(event.packet);
|
||||
response_received = true;
|
||||
},
|
||||
|
||||
enet.c.ENET_EVENT_TYPE_DISCONNECT => {
|
||||
std.debug.print("Server disconnected\n", .{});
|
||||
connected = false;
|
||||
break;
|
||||
},
|
||||
|
||||
else => {},
|
||||
}
|
||||
} else if (result < 0) {
|
||||
std.debug.print("Error servicing host\n", .{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!response_received and connected) {
|
||||
std.debug.print("No response received for message {}\n", .{i + 1});
|
||||
}
|
||||
|
||||
if (!connected) break;
|
||||
|
||||
// Small delay between messages
|
||||
std.time.sleep(100 * std.time.ns_per_ms);
|
||||
}
|
||||
|
||||
// Disconnect if still connected
|
||||
if (connected) {
|
||||
std.debug.print("Disconnecting from server...\n", .{});
|
||||
enet.c.enet_peer_disconnect(peer, 0);
|
||||
|
||||
// Wait for disconnection to complete
|
||||
var disconnected = false;
|
||||
var disconnect_attempts: u32 = 0;
|
||||
const max_disconnect_attempts = 10;
|
||||
|
||||
while (!disconnected and disconnect_attempts < max_disconnect_attempts) {
|
||||
const result = enet.c.enet_host_service(host, &event, 500);
|
||||
disconnect_attempts += 1;
|
||||
|
||||
if (result > 0 and event.type == enet.c.ENET_EVENT_TYPE_DISCONNECT) {
|
||||
std.debug.print("Disconnected successfully\n", .{});
|
||||
disconnected = true;
|
||||
} else if (result < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!disconnected) {
|
||||
std.debug.print("Force disconnecting...\n", .{});
|
||||
enet.c.enet_peer_reset(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std.debug.print("Client shutting down\n", .{});
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
const std = @import("std");
|
||||
const enet = @import("enet");
|
||||
|
||||
const enetc = enet.c;
|
||||
|
||||
const SERVER_PORT = 7777;
|
||||
const MAX_CLIENTS = 32;
|
||||
const CHANNEL_COUNT = 2;
|
||||
|
||||
pub fn main() !void {
|
||||
std.debug.print("Starting ENet test server on port {}\n", .{SERVER_PORT});
|
||||
|
||||
// Initialize ENet
|
||||
try enet.initialize();
|
||||
defer enet.deinitialize();
|
||||
|
||||
// Create server address
|
||||
var address = enetc.ENetAddress{
|
||||
.host = enetc.ENET_HOST_ANY,
|
||||
.port = SERVER_PORT,
|
||||
};
|
||||
|
||||
// Create host
|
||||
const host = enetc.enet_host_create(&address, MAX_CLIENTS, CHANNEL_COUNT, 0, 0) orelse {
|
||||
std.debug.print("Failed to create ENet server host\n", .{});
|
||||
return error.HostCreationFailed;
|
||||
};
|
||||
defer enetc.enet_host_destroy(host);
|
||||
|
||||
std.debug.print("ENet server listening on port {} (waiting for clients...)\n", .{SERVER_PORT});
|
||||
|
||||
var running = true;
|
||||
var client_count: u32 = 0;
|
||||
var message_count: u32 = 0;
|
||||
|
||||
while (running) {
|
||||
var event: enetc.ENetEvent = undefined;
|
||||
|
||||
// Service the host with a 1000ms timeout
|
||||
const result = enetc.enet_host_service(host, &event, 1000);
|
||||
|
||||
if (result > 0) {
|
||||
switch (event.type) {
|
||||
enetc.ENET_EVENT_TYPE_CONNECT => {
|
||||
client_count += 1;
|
||||
std.debug.print("Client connected from {}:{} (total clients: {})\n", .{ event.peer.*.address.host, event.peer.*.address.port, client_count });
|
||||
|
||||
// Send welcome message
|
||||
const welcome_msg = "Welcome to ENet test server!";
|
||||
if (enetc.enet_packet_create(welcome_msg.ptr, welcome_msg.len, enetc.ENET_PACKET_FLAG_RELIABLE)) |packet| {
|
||||
_ = enetc.enet_peer_send(event.peer, 0, packet);
|
||||
}
|
||||
},
|
||||
|
||||
enetc.ENET_EVENT_TYPE_RECEIVE => {
|
||||
message_count += 1;
|
||||
const data = @as([*]u8, @ptrCast(event.packet.*.data))[0..event.packet.*.dataLength];
|
||||
std.debug.print("Received message #{}: '{s}'\n", .{ message_count, data });
|
||||
|
||||
// Echo the message back to the client
|
||||
const echo_msg = std.fmt.allocPrint(std.heap.page_allocator, "Echo: {s}", .{data}) catch "Echo: (allocation failed)";
|
||||
defer if (!std.mem.eql(u8, echo_msg, "Echo: (allocation failed)")) std.heap.page_allocator.free(echo_msg);
|
||||
|
||||
if (enetc.enet_packet_create(echo_msg.ptr, echo_msg.len, enetc.ENET_PACKET_FLAG_RELIABLE)) |packet| {
|
||||
_ = enetc.enet_peer_send(event.peer, 0, packet);
|
||||
}
|
||||
|
||||
// Destroy the received packet
|
||||
enetc.enet_packet_destroy(event.packet);
|
||||
|
||||
// If this is a "quit" message, stop the server
|
||||
if (std.mem.eql(u8, data, "quit")) {
|
||||
std.debug.print("Received quit command, shutting down server...\n", .{});
|
||||
running = false;
|
||||
}
|
||||
},
|
||||
|
||||
enetc.ENET_EVENT_TYPE_DISCONNECT => {
|
||||
client_count -= 1;
|
||||
std.debug.print("Client disconnected (total clients: {})\n", .{client_count});
|
||||
event.peer.*.data = null;
|
||||
},
|
||||
|
||||
else => {},
|
||||
}
|
||||
} else if (result < 0) {
|
||||
std.debug.print("Error servicing host\n", .{});
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop server if no clients and we've processed some messages
|
||||
if (client_count == 0 and message_count > 0) {
|
||||
std.debug.print("No clients connected, shutting down...\n", .{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std.debug.print("Server shutting down. Processed {} messages from {} client connections.\n", .{ message_count, client_count });
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
pub const enet = @import("enet");
|
||||
|
||||
test "testing" {
|
||||
std.debug.print("testing enet\n", .{});
|
||||
|
||||
try enet.initialize();
|
||||
defer enet.deinitialize();
|
||||
std.debug.print("initialized\n", .{});
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
|
@ -10,6 +10,8 @@ pub const BumpArena = struct {
|
|||
smallBumpOffset: usize = 0,
|
||||
pageIndex: usize = 0,
|
||||
|
||||
allocatedBytes: usize = 0,
|
||||
|
||||
backing: std.mem.Allocator,
|
||||
small: SmallAllocPages,
|
||||
large: std.heap.ArenaAllocator,
|
||||
|
|
@ -22,6 +24,10 @@ pub const BumpArena = struct {
|
|||
.remap = std.mem.Allocator.noRemap,
|
||||
};
|
||||
|
||||
pub fn usage(self: *@This()) usize {
|
||||
return self.allocatedBytes;
|
||||
}
|
||||
|
||||
pub fn init(backing: std.mem.Allocator) !@This() {
|
||||
return .{
|
||||
.backing = backing,
|
||||
|
|
@ -52,6 +58,8 @@ pub const BumpArena = struct {
|
|||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
|
||||
self.allocatedBytes += len;
|
||||
|
||||
if (len > 4096) {
|
||||
return self.largeAllocator().vtable.alloc(self.largeAllocator().ptr, len, ptr_align, ret_addr);
|
||||
} else {
|
||||
|
|
@ -108,7 +116,6 @@ pub const BumpArena = struct {
|
|||
self.large.reset(.free_all);
|
||||
self.small.reset(self.backing);
|
||||
self.smallBumpOffset = 0;
|
||||
self.latestPageFree = PageSize;
|
||||
self.pageIndex = 0;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -175,16 +175,16 @@ pub const Name = struct {
|
|||
return getRegistry().pagedVector.get(self.index.?).*;
|
||||
}
|
||||
|
||||
pub fn eql(self: *const @This(), other: *const @This()) bool {
|
||||
pub fn eql(self: *@This(), other: *@This()) bool {
|
||||
if (self.index == null) {
|
||||
const index = getRegistry().InstallNameInner(self.string, false);
|
||||
const mutableThis = @as(*@This(), @ptrCast(@constCast(self)));
|
||||
const mutableThis = self;
|
||||
mutableThis.*.index = index;
|
||||
}
|
||||
|
||||
if (other.index == null) {
|
||||
const index = getRegistry().InstallNameInner(self.string, false);
|
||||
const mutableThis = @as(*@This(), @ptrCast(@constCast(self)));
|
||||
const mutableThis = self;
|
||||
mutableThis.*.index = index;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -555,6 +555,10 @@ pub fn SparseSetAdvanced(comptime T: type, comptime SparseSize: u32) type {
|
|||
self.dense.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn denseList(self: @This()) []const T {
|
||||
return self.dense.items;
|
||||
}
|
||||
|
||||
pub const EcsContainerInterfaceVTable = EcsContainerInterface.Implement(@This());
|
||||
|
||||
pub fn handleExists(self: @This(), handle: SetHandle) bool {
|
||||
|
|
|
|||
|
|
@ -164,7 +164,6 @@ pub const PackerFS = struct {
|
|||
|
||||
pub fn watchCallback(path: [*c]const u8, ctx: ?*anyopaque) callconv(.C) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(ctx));
|
||||
// std.debug.print("WE GOT OURSELFS A FUCKIN CALLBAKC FOR A PATH MOTHERF- {s} {p} {d}\n", .{ path, ctx.?, self.pakMountings.items.len });
|
||||
|
||||
for (self.fileWatchCallbacks.items) |*watch| {
|
||||
// std.debug.print("checking {s} {s}\n", .{ watch.path, path });
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub fn build(b: *std.Build) void {
|
|||
sampleGame.setModuleEnabled("physics", true);
|
||||
sampleGame.setModuleEnabled("ui", true);
|
||||
sampleGame.setModuleEnabled("sys", true);
|
||||
sampleGame.setModuleEnabled("net", true);
|
||||
sampleGame.addExtraModule("gameExtras");
|
||||
sampleGame.addExtraModule("videoplayer");
|
||||
sampleGame.addExtraModule("doomplayer");
|
||||
|
|
@ -32,14 +33,9 @@ pub fn build(b: *std.Build) void {
|
|||
|
||||
_ = externGame.compileInstall();
|
||||
}
|
||||
sampleGame.setIconPath("icons/icon.ico");
|
||||
const sampleGameExe = sampleGame.compileInstall();
|
||||
|
||||
if (target.result.os.tag == .windows) {
|
||||
sampleGameExe.addWin32ResourceFile(.{
|
||||
.file = b.path("sampleGame/sampleGame.rc"),
|
||||
.flags = &.{},
|
||||
});
|
||||
}
|
||||
_ = sampleGameExe;
|
||||
|
||||
// tools
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
.sdl3 = .{ .path = "../lib/sdl3" },
|
||||
.miniaudio = .{ .path = "../lib/miniaudio" },
|
||||
.lua = .{ .path = "../lib/lua" },
|
||||
|
||||
.enet = .{ .path = "../lib/enet" },
|
||||
},
|
||||
.paths = .{
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ pub export fn startup(p_allocator: *anyopaque, p_a: ?*anyopaque) bool {
|
|||
sys.setupFromModule();
|
||||
rend.setupFromModule();
|
||||
backlog.physics.setupFromModule();
|
||||
// backlog.net.setupFromModule();
|
||||
// TODO backlog.setupFromModule
|
||||
|
||||
core.engine_logs("creating externgame");
|
||||
start_module(core.startup_getArgs(p_a.?)) catch return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -26,6 +28,7 @@ var gAllocator: std.mem.Allocator = undefined;
|
|||
pub fn start_module(args: core.ModuleLoaderArgs) !void {
|
||||
if (args.firstLoad) {
|
||||
_ = core.createObject(ExternGameObject, .{}) catch {};
|
||||
core.engine_logs("creating externgame");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +43,54 @@ pub fn start_module(args: core.ModuleLoaderArgs) !void {
|
|||
core.PatchStruct(ExternGameObject, @ptrCast(@alignCast(ref.ptr)), ref.vtable.fieldList.?);
|
||||
}
|
||||
|
||||
pub const BoxObject = struct {
|
||||
playing: bool = false,
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator, entity: core.Entity, params: core.SpawnParameters) !*@This() {
|
||||
const position = params.posRot.position;
|
||||
|
||||
const box2 = entity;
|
||||
const scene = box2.addComponent(core.Scene).?;
|
||||
|
||||
const mesh = box2.addComponent(rend.MeshComponent).?;
|
||||
mesh.setMesh("m_crate");
|
||||
mesh.setTexture("t_crate");
|
||||
|
||||
scene.setPosition(position);
|
||||
scene.setScaleV(params.posRot.scale);
|
||||
scene.setMobility(.moveable);
|
||||
|
||||
const collider = box2.addComponent(physics.PhysicsCollider).?;
|
||||
try collider.setupByShapeName(core.MakeName("ph_small_box"), .{
|
||||
.motion_type = .dynamic,
|
||||
.object_layer = physics.ObjectLayers.moving,
|
||||
.friction = 0.8,
|
||||
.mass_properties_override = .{ .mass = 12 },
|
||||
.override_mass_properties = .calc_inertia,
|
||||
});
|
||||
|
||||
return try allocator.create(@This());
|
||||
}
|
||||
|
||||
pub fn createSmoky(allocator: std.mem.Allocator, entity: core.Entity, params: core.SpawnParameters) !*@This() {
|
||||
const rv = try create(allocator, entity, params);
|
||||
|
||||
const particle = entity.addComponent(rend.ParticleEmitter).?;
|
||||
particle.start();
|
||||
particle.gravity = .{ .y = 1.0 };
|
||||
particle.particleVelocity = .{ .min = 0.6, .max = 1.2 };
|
||||
particle.particleLife = .{ .min = 5, .max = 6 };
|
||||
var name = core.MakeName("t_pixelSmoke");
|
||||
particle.texture = rend.getTexture(&name).?;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This(), alloc: std.mem.Allocator) void {
|
||||
alloc.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const ExternGameObject = struct {
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "game.ExternGameObject");
|
||||
pub const Slack = core.SlackStruct(@This(), 512);
|
||||
|
|
@ -51,11 +102,58 @@ pub const ExternGameObject = struct {
|
|||
fireTraceFilter: physics.IgnoreFixedBodiesFilter = .{},
|
||||
particleDebugger: extras.ParticleDebugger.ParticleDebugger = undefined,
|
||||
|
||||
session: ?*net.Session = null,
|
||||
clientLink: ?*net.Link = null,
|
||||
|
||||
volume: f32 = 100,
|
||||
//lmao: bool = false,
|
||||
//lmao2: bool = true,
|
||||
a: bool = false,
|
||||
// lib: bool = false,
|
||||
//s: u32 = 0x42,
|
||||
//
|
||||
firstTick: bool = true,
|
||||
|
||||
fireInput: ?*core.ActionBinding = null,
|
||||
altFireInput: ?*core.ActionBinding = null,
|
||||
|
||||
a: core.Vector2f = .{ .x = 1, .y = 4 },
|
||||
b: core.Vector2f = .{ .x = 4, .y = 1 },
|
||||
|
||||
Anormal: core.Vector2f = .{ .x = -1, .y = 3 },
|
||||
Bnormal: core.Vector2f = .{ .x = -1, .y = -3 },
|
||||
|
||||
spawnedEntities: std.ArrayListUnmanaged(core.Entity) = .{},
|
||||
|
||||
displayVectorsTest: bool = false,
|
||||
|
||||
pub fn beginPlay(p: *anyopaque) void {
|
||||
const self = core.cast(*@This(), p);
|
||||
self._beginPlay() catch {
|
||||
core.engine_errs("unable to beginplay");
|
||||
};
|
||||
}
|
||||
|
||||
pub fn _beginPlay(self: *@This()) !void {
|
||||
if (self.fireInput == null) {
|
||||
self.fireInput = try core.ActionBinding.create(core.MakeName("fire"));
|
||||
self.fireInput.?.addKey(.Mouse1, .keyDown);
|
||||
_ = self.fireInput.?.data.addListener(self, onFire);
|
||||
}
|
||||
self.fireInput.?.activate();
|
||||
|
||||
if (self.altFireInput == null) {
|
||||
self.altFireInput = try core.ActionBinding.create(core.MakeName("altFire"));
|
||||
self.altFireInput.?.addKey(.Mouse3, .keyDown);
|
||||
_ = self.altFireInput.?.data.addListener(self, onAltFire);
|
||||
}
|
||||
self.altFireInput.?.activate();
|
||||
}
|
||||
|
||||
pub fn endPlay(p: *anyopaque) void {
|
||||
const self = core.cast(*@This(), p);
|
||||
self.fireInput.?.deactivate();
|
||||
self.altFireInput.?.deactivate();
|
||||
}
|
||||
|
||||
pub fn loadMap2(self: *@This()) !void {
|
||||
if (self.tbMap) |tbMap| {
|
||||
|
|
@ -84,15 +182,8 @@ pub const ExternGameObject = struct {
|
|||
self.physicsObjectsWindow = try extras.PhysicsObjectList.create(self.allocator);
|
||||
self.consoleWindow.setup();
|
||||
|
||||
const fireInput = try core.ActionBinding.create(core.MakeName("fire"));
|
||||
fireInput.addKey(.Mouse1, .keyDown);
|
||||
_ = fireInput.data.addListener(self, onFire);
|
||||
fireInput.activate();
|
||||
|
||||
const altFireInput = try core.ActionBinding.create(core.MakeName("altFire"));
|
||||
altFireInput.addKey(.Mouse3, .keyDown);
|
||||
_ = altFireInput.data.addListener(self, onAltFire);
|
||||
altFireInput.activate();
|
||||
try core.registerObject(BoxObject, "Box");
|
||||
try core.registerObjectAdvanced(BoxObject, "BoxSmoky", "createSmoky");
|
||||
|
||||
const settings: physics.ShapeSettings = .{ .box = try physics.BoxShapeSettings.create(.{ 1.0, 1.0, 1.0 }) };
|
||||
defer settings.release();
|
||||
|
|
@ -107,48 +198,103 @@ pub const ExternGameObject = struct {
|
|||
//ctx.get(panel2).anchor = .TopRight;
|
||||
// ctx.get(panel2).fill = .FillY; todo, this is a bug. why.
|
||||
ctx.get(panel2).pos = .{ .x = 900, .y = 200 };
|
||||
ctx.get(panel2).size = .{ .x = 300, .y = 400 };
|
||||
ctx.get(panel2).size = .{ .x = 300, .y = 500 };
|
||||
ctx.get(panel2).style.backgroundColor = ui.ModernStyle.GreyDark;
|
||||
ctx.getPanel(panel2).titleColor = ui.ModernStyle.GreyDark;
|
||||
ctx.getPanel(panel2).layoutMode = .Vertical;
|
||||
|
||||
// add some text to this panel
|
||||
const text = try ctx.addText(panel2, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
|
||||
const text = try ctx.addText(panel2, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
|
||||
ctx.get(text).pos = .{ .x = 2, .y = 2 };
|
||||
ctx.get(text).size = ctx.get(panel2).size.sub(.{ .x = 4, .y = 36 });
|
||||
ctx.get(text).size = .{ .x = 296, .y = 300 };
|
||||
|
||||
const btn = try ctx.addButton(panel2, "select file...");
|
||||
_ = btn;
|
||||
}
|
||||
}
|
||||
|
||||
try extras.games.addGame("ExternGame", self, @This());
|
||||
|
||||
extras.beginGame("ExternGame");
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn addBox(position: core.Vectorf) !void {
|
||||
const box2 = try core.createEntity();
|
||||
const scene = box2.addComponent(core.Scene).?;
|
||||
fn editVector2(comptime Name: []const u8, v: *core.Vector2f) void {
|
||||
const ItemWidth = 120;
|
||||
|
||||
const mesh = box2.addComponent(rend.MeshComponent).?;
|
||||
mesh.setMesh("m_crate");
|
||||
mesh.setTexture("t_crate");
|
||||
ig.textFmt(Name, .{}) catch {};
|
||||
ig.sameLine(0, 10);
|
||||
ig.setNextItemWidth(ItemWidth);
|
||||
_ = ig.inputFloat("X##particleEd" ++ Name, &v.x, 1.0, 5.0, null, .{});
|
||||
ig.sameLine(0, 10);
|
||||
ig.setNextItemWidth(ItemWidth);
|
||||
_ = ig.inputFloat("Y##particleEd" ++ Name, &v.y, 1.0, 5.0, null, .{});
|
||||
}
|
||||
|
||||
scene.setPosition(position);
|
||||
scene.setScaleV(core.Vectorf.fromInt(1.0));
|
||||
scene.setMobility(.moveable);
|
||||
fn v2ToV3(v: core.Vector2f) core.Vectorf {
|
||||
return core.Vectorf{ .x = v.x, .z = v.y, .y = 0 };
|
||||
}
|
||||
|
||||
const collider = box2.addComponent(physics.PhysicsCollider).?;
|
||||
try collider.setupByShapeName(core.MakeName("ph_small_box"), .{
|
||||
.motion_type = .dynamic,
|
||||
.object_layer = physics.ObjectLayers.moving,
|
||||
.friction = 0.8,
|
||||
.mass_properties_override = .{ .mass = 12 },
|
||||
.override_mass_properties = .calc_inertia,
|
||||
});
|
||||
pub fn intersectLine(p1: core.Vector2f, v1: core.Vector2f, p2: core.Vector2f, v2: core.Vector2f) ?core.Vector2f {
|
||||
const denom = v1.x * v2.y - v1.y * v2.x;
|
||||
|
||||
const particle = box2.addComponent(rend.ParticleEmitter).?;
|
||||
particle.start();
|
||||
particle.gravity = .{ .y = 1.0 };
|
||||
particle.particleVelocity = .{ .min = 0.6, .max = 1.2 };
|
||||
particle.particleLife = .{ .min = 5, .max = 6 };
|
||||
var name = core.MakeName("t_pixelSmoke");
|
||||
particle.texture = rend.getTexture(&name).?;
|
||||
if (@abs(denom) < 0.00001) {
|
||||
return null;
|
||||
}
|
||||
const t = ((p2.x - p1.x) * v2.y - (p2.y - p1.y) * v2.x) / denom;
|
||||
|
||||
return .{
|
||||
.x = p1.x + t * v1.x,
|
||||
.y = p1.y + t * v1.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn drawVector2Test(self: *@This()) void {
|
||||
if (ig.begin("vector2Test", null, .{})) {
|
||||
editVector2("a", &self.a);
|
||||
editVector2("Anormal", &self.Anormal);
|
||||
ig.separator();
|
||||
editVector2("b", &self.b);
|
||||
editVector2("Bnormal", &self.Bnormal);
|
||||
}
|
||||
ig.end();
|
||||
|
||||
const a = core.Vectorf{ .x = self.a.x, .z = self.a.y, .y = 0 };
|
||||
const b = core.Vectorf{ .x = self.b.x, .z = self.b.y, .y = 0 };
|
||||
|
||||
const A = core.Vectorf{ .x = self.Anormal.x, .z = self.Anormal.y, .y = 0 };
|
||||
const B = core.Vectorf{ .x = self.Bnormal.x, .z = self.Bnormal.y, .y = 0 };
|
||||
|
||||
core.debugSphere(a, 0.1, .{});
|
||||
core.debugSphere(b, 0.1, .{});
|
||||
|
||||
core.debugLine(a, a.add(A), .{ .color = .{ .y = 0, .x = 1.0 } });
|
||||
core.debugLine(b, b.add(B), .{ .color = .{ .y = 0, .x = 1.0 } });
|
||||
|
||||
const Vab = self.b.sub(self.a);
|
||||
const Vba = Vab.negate();
|
||||
|
||||
core.debugLine(a, a.add(v2ToV3(Vab)), .{ .color = .{ .x = 1.0, .z = 1.0 } });
|
||||
// const ADwall = Vab.dot(self.Anormal.removeComponent(Vab.normalize()).normalize());
|
||||
// const BDwall = Vba.dot(self.Anormal.removeComponent(Vba.normalize()).normalize());
|
||||
|
||||
//const Awall = Vab.removeComponent(self.Anormal.normalize()).normalize().fmul(ADwall).normalize();
|
||||
const Awall = Vab.removeComponent(self.Anormal.normalize()).normalize();
|
||||
const Bwall = Vba.removeComponent(self.Bnormal.normalize()).normalize();
|
||||
|
||||
//ig.textFmt("Awall: {d} {d}", .{ Awall.x, Awall.y }) catch {};
|
||||
core.debugLine(a.sub(v2ToV3(Awall).fmul(10)), a.add(v2ToV3(Awall).fmul(10)), .{ .color = .{ .y = 1.0, .z = 1.0 } });
|
||||
|
||||
// ig.textFmt("Bwall: {d} {d}", .{ Bwall.x, Bwall.y }) catch {};
|
||||
core.debugLine(b.sub(v2ToV3(Bwall).fmul(10)), b.add(v2ToV3(Bwall).fmul(10)), .{ .color = .{ .y = 1.0, .z = 1.0 } });
|
||||
|
||||
const vp = intersectLine(self.a, Awall.normalize(), self.b, Bwall.normalize()) orelse {
|
||||
return;
|
||||
};
|
||||
|
||||
// ig.textFmt("p, y: {d} {d}", .{ vp.x, vp.y }) catch {};
|
||||
core.debugSphere(v2ToV3(vp), 0.1, .{ .color = .{ .y = 1.0, .z = 1.0, .x = 1.0 } });
|
||||
}
|
||||
|
||||
fn onAltFire(ctx: ?*anyopaque, _: core.ActionEvent) void {
|
||||
|
|
@ -178,9 +324,14 @@ pub const ExternGameObject = struct {
|
|||
const ray = rend.context().activeCamera.?.getNormalRayFromScreen(platform.getCursorPosition());
|
||||
if (physics.traceLine(ray.start, ray.dir.fmul(10000), .{ .bodyFilter = &self.fireTraceFilter, .getNormalsSlow = true })) |r| {
|
||||
if (r.normal) |n| {
|
||||
const p = r.point.add(n.fmul(0.2));
|
||||
const p: core.Vectorf = r.point.add(n.fmul(0.2));
|
||||
|
||||
addBox(p.add(n.fmul(1.0))) catch unreachable;
|
||||
const posRot = core.ScenePosRot{
|
||||
.position = p,
|
||||
};
|
||||
self.spawnedEntities.append(self.allocator, core.get(extras.ObjectSystemSpawner).spawnObjectAt(posRot) orelse return) catch unreachable;
|
||||
|
||||
// addBox(p.add(n.fmul(1.0))) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,6 +346,15 @@ pub const ExternGameObject = struct {
|
|||
topbar.menuOpen = platform.context().isCursorEnabled();
|
||||
}
|
||||
|
||||
if (self.displayVectorsTest) {
|
||||
self.drawVector2Test();
|
||||
}
|
||||
|
||||
if (self.firstTick) {
|
||||
self.loadMap2() catch unreachable;
|
||||
self.firstTick = false;
|
||||
}
|
||||
|
||||
if (platform.context().isCursorEnabled()) {
|
||||
if (ig.begin("meh", null, .{})) {
|
||||
// if (ig.checkbox("move lights ", null)) {}
|
||||
|
|
@ -219,6 +379,32 @@ pub const ExternGameObject = struct {
|
|||
if (ig.smallButton("dumpTimeline")) {
|
||||
core.MemoryTracker.dumpTimeline("timeline.txt") catch unreachable;
|
||||
}
|
||||
|
||||
if (ig.checkbox("vectors test", &self.displayVectorsTest)) {}
|
||||
if (ig.checkbox("showdebug", &ui.context().screenContext.drawDebug)) {}
|
||||
|
||||
if (ig.smallButton("click for fun")) {
|
||||
// var soundname = core.MakeName("s_macintosh");
|
||||
// audio.context().playSound(&soundname) catch {};
|
||||
}
|
||||
|
||||
if (ig.sliderFloat("sliderFloat", &self.volume, 0, 100, null, .{})) {
|
||||
audio.context().setVolume(self.volume / 100);
|
||||
}
|
||||
|
||||
if (ig.smallButton("host")) {
|
||||
self.session = net.hostSession(&.{"7777"}) catch unreachable;
|
||||
}
|
||||
|
||||
if (ig.smallButton("connect")) {
|
||||
self.clientLink = net.connect("127.0.0.1:7777") catch unreachable;
|
||||
}
|
||||
|
||||
if (self.clientLink) |link| {
|
||||
if (ig.smallButton("sendMessageToServer")) {
|
||||
link.sendMessage("hello from client", true) catch {};
|
||||
}
|
||||
}
|
||||
}
|
||||
ig.end();
|
||||
|
||||
|
|
@ -233,12 +419,40 @@ pub const ExternGameObject = struct {
|
|||
}
|
||||
self.particleDebugger.particleDebug();
|
||||
}
|
||||
if (self.session) |session| {
|
||||
if (session.links.items.len > 0) {
|
||||
const linkData = net.EnetTransport.getLinkData(session.links.items[0]);
|
||||
if (rend.context().activeCamera) |camera| {
|
||||
linkData.testLinkPosition = camera.entity.fetch(core.Scene).?.getPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (self.clientLink) |link| {
|
||||
const linkData = net.EnetTransport.getLinkData(link);
|
||||
if (self.spawnedEntities.items.len > 0) {
|
||||
self.spawnedEntities.items[0].fetch(core.Scene).?.setPosition(linkData.testLinkPosition);
|
||||
}
|
||||
core.debugSphere(linkData.testLinkPosition, 1.0, .{});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
if (self.clientLink) |link| {
|
||||
link.destroy();
|
||||
}
|
||||
|
||||
if (self.session) |session| {
|
||||
session.destroy();
|
||||
}
|
||||
|
||||
if (self.tbMap) |map| {
|
||||
map.destroy();
|
||||
}
|
||||
for (self.spawnedEntities.items) |*entity| {
|
||||
entity.destroy();
|
||||
}
|
||||
|
||||
self.spawnedEntities.deinit(self.allocator);
|
||||
self.consoleWindow.deinit();
|
||||
self.physicsObjectsWindow.destroy();
|
||||
self.allocator.destroy(Slack.fromPtr(self));
|
||||
|
|
@ -256,6 +470,8 @@ const rend = backlog.rend;
|
|||
const ig = imgui.api;
|
||||
const sys = backlog.sys;
|
||||
const ui = backlog.ui;
|
||||
const net = backlog.net;
|
||||
const audio = backlog.audio;
|
||||
const platform = backlog.platform;
|
||||
const extras = @import("gameExtras");
|
||||
const bsp = @import("bsp");
|
||||
|
|
|
|||
|
|
@ -58,6 +58,11 @@ const assetReferences = [_]assets.AssetImportReference{
|
|||
"a_fox_survey",
|
||||
.{ .path = "gltf-samples/Fox/glTF/Survey.ozz" },
|
||||
),
|
||||
// assets.MakeImportRefOptions(
|
||||
// "Sound",
|
||||
// "s_macintosh",
|
||||
// .{ .path = "sounds/MACINTOSHPLUS.wav" },
|
||||
// ),
|
||||
assets.MakeImportRefOptions(
|
||||
"Mesh",
|
||||
"m_fox",
|
||||
|
|
@ -149,15 +154,23 @@ pub const ParticleObject = struct {
|
|||
};
|
||||
|
||||
pub const FoxObject = struct {
|
||||
data: core.GameObjectData = undefined,
|
||||
entity: core.Entity,
|
||||
|
||||
pub fn create(alloc: std.mem.Allocator) !*@This() {
|
||||
pub const MessageHandlers: []const []const u8 = &.{
|
||||
"onGameModeMessage",
|
||||
};
|
||||
|
||||
pub fn create(alloc: std.mem.Allocator, entity: core.Entity, p: core.SpawnParameters) !*@This() {
|
||||
const self = try alloc.create(@This());
|
||||
const fox = try core.createEntity();
|
||||
//const fox = try core.createEntity();
|
||||
const fox = entity;
|
||||
const scene = fox.addComponent(core.Scene).?;
|
||||
|
||||
scene.setScale(0.01, 0.01, 0.01);
|
||||
var pr = p.posRot;
|
||||
pr.scale = pr.scale.fmul(0.01);
|
||||
scene.setPosRot(pr);
|
||||
scene.updateTransform();
|
||||
|
||||
const mesh = fox.addComponent(rend.MeshComponent).?;
|
||||
mesh.setMesh("m_fox");
|
||||
mesh.setTexture("t_fox");
|
||||
|
|
@ -171,13 +184,19 @@ pub const FoxObject = struct {
|
|||
return self;
|
||||
}
|
||||
|
||||
pub fn onGameModeMessage(self: *@This(), messageInfo: *const core.MessageInfo, gameModeMessage: *extras.GameModeMessage) void {
|
||||
_ = self;
|
||||
_ = messageInfo;
|
||||
_ = gameModeMessage;
|
||||
}
|
||||
|
||||
pub fn getEntity(self: *@This()) core.Entity {
|
||||
return self.entity;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.entity.destroy();
|
||||
self.data.release(self);
|
||||
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
|
||||
allocator.destroy(self);
|
||||
core.engine_logs("destroying fox");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -231,6 +250,10 @@ pub fn prepare(self: *@This()) !void {
|
|||
defer z.End();
|
||||
try core.fs().addContentPath("sampleGame");
|
||||
|
||||
try extras.setup();
|
||||
|
||||
try audio.loadBuiltinSounds();
|
||||
|
||||
// try core.loadModule("externGame", true);
|
||||
|
||||
try backlog.loadDynamicModules();
|
||||
|
|
@ -240,28 +263,40 @@ pub fn prepare(self: *@This()) !void {
|
|||
// core.fs().watchPath("zig-out/modules");
|
||||
// try core.fs().addFileChangedCallback(core.sharedLibName("externGame"), moduleChangedCallback, self);
|
||||
|
||||
// try self.tryLoadExtern("externGame");
|
||||
|
||||
// self.rendererDebugger = try extras.RendererDebug.create(self.allocator); //try core.createObject(extras.RendererDebug, .{});
|
||||
const rendererDebug = try core.createObject(extras.RendererDebug, .{});
|
||||
try rendererDebug.skyboxes.append(rendererDebug.allocator, core.MakeName("t_skybox"));
|
||||
try rendererDebug.skyboxes.append(rendererDebug.allocator, core.MakeName("t_dark_skybox"));
|
||||
|
||||
const objectSpawner2 = try core.createObject(extras.ObjectSystemSpawner, .{});
|
||||
_ = objectSpawner2;
|
||||
|
||||
//self.objectSpawner = try extras.ObjectSpawner.create(self.allocator);
|
||||
self.objectSpawner = try core.createObject(extras.ObjectSpawner, .{});
|
||||
try self.objectSpawner.addSpawnFunction("fox", FoxObject);
|
||||
// try self.objectSpawner.addSpawnFunction("fox", FoxObject);
|
||||
try self.objectSpawner.addSpawnFunction("particle", ParticleObject);
|
||||
try self.objectSpawner.addSpawnFunction("doomplayer", DoomPlayer.DoomCanvas);
|
||||
try self.objectSpawner.addSpawnFunction("empire", @import("empire.zig"));
|
||||
try self.objectSpawner.addSpawnFunction("DamagedHelmet", DamagedHelmet);
|
||||
|
||||
try core.getEngineObject(core.GameObjectSystem).?.registerObject("foxObject", FoxObject);
|
||||
|
||||
try assets.loadList(assetReferences);
|
||||
// self.videoplayer = try VideoPlayer.create(self.allocator);
|
||||
// try self.videoplayer.startPlayback("LAPWING2.ogv");
|
||||
// self.videoplayerObject = try self.videoplayer.createVideoPlayerEntity();
|
||||
|
||||
rend.setSkyboxTexture("t_skybox");
|
||||
beginPlay(@ptrCast(self));
|
||||
}
|
||||
|
||||
fn beginPlay(p: ?*anyopaque) void {
|
||||
const self = core.cast(*@This(), p.?);
|
||||
self._beginPlay() catch {
|
||||
core.engine_errs("unable to beginPlay sampleagme");
|
||||
};
|
||||
}
|
||||
|
||||
fn _beginPlay(self: *@This()) !void {
|
||||
const exitInput = try core.ActionBinding.create(core.MakeName("exit"));
|
||||
exitInput.addKey(.escape, .keyDown);
|
||||
_ = exitInput.data.addListener(null, onExit);
|
||||
|
|
@ -334,9 +369,15 @@ pub fn prepare(self: *@This()) !void {
|
|||
_ = input.data.addListener(self, toggleMouseLook);
|
||||
input.activate();
|
||||
}
|
||||
|
||||
self.updateMouseLook();
|
||||
}
|
||||
|
||||
fn game_endPlay(p: ?*anyopaque) void {
|
||||
const self = core.cast(@This(), p);
|
||||
_ = self;
|
||||
}
|
||||
|
||||
fn onMouseLook(ctx: ?*anyopaque, axis: core.Vector2f) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(ctx));
|
||||
|
||||
|
|
@ -521,6 +562,7 @@ const assets = backlog.assets;
|
|||
const platform = backlog.platform;
|
||||
const core = backlog.core;
|
||||
const ig = backlog.imgui.api;
|
||||
const audio = backlog.audio;
|
||||
const rend = backlog.rend;
|
||||
const script = core.script;
|
||||
const tracy = core.tracy;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
exitInput: ?*core.ActionBinding = null,
|
||||
|
||||
const std = @import("std");
|
||||
const backlog = @import("backlog");
|
||||
const assets = backlog.assets;
|
||||
const platform = backlog.platform;
|
||||
const core = backlog.core;
|
||||
const ig = backlog.imgui.api;
|
||||
const rend = backlog.rend;
|
||||
const script = core.script;
|
||||
const tracy = core.tracy;
|
||||
const ui = backlog.ui;
|
||||
Loading…
Reference in New Issue