From 2447400dc76ba97720c07ce8f9775d2f4e68d5ae Mon Sep 17 00:00:00 2001 From: DoomIsEternal <65947351+DoomIsEternal@users.noreply.github.com> Date: Sat, 20 Jan 2024 01:51:13 +0200 Subject: [PATCH] v0.4.4 * Improved tests to handle more cases * Simplified generator assert generation * When a function fails on the server it will now output the error message * Event On now returns a disconnect function * Various fixes --- .gitignore | 2 -- src/Generator/Prefabs.luau | 42 +++++++++++++++++++++++++----- src/Generator/init.luau | 21 +++++++-------- src/Templates/Base.txt | 7 +++-- src/Templates/Client.txt | 1 + test/Client.luau | 46 +++++++++++++++++++++++++++++++++ test/Server.luau | 52 ++++++++++++++++++++++++++++++++++++++ test/Sources/Test.txt | 12 ++++----- test/Test.luau | 26 +++++++++++-------- 9 files changed, 171 insertions(+), 38 deletions(-) create mode 100644 test/Client.luau create mode 100644 test/Server.luau diff --git a/.gitignore b/.gitignore index 27ae978..a778da5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,4 @@ /release /Network /Blink -test/Client.luau -test/Server.luau test/Sources/Game.txt diff --git a/src/Generator/Prefabs.luau b/src/Generator/Prefabs.luau index a12a523..6e398da 100644 --- a/src/Generator/Prefabs.luau +++ b/src/Generator/Prefabs.luau @@ -23,6 +23,7 @@ type AssertGenerator = ((Variable: string, Min: number, Max: number) -> AssertPr local SEND_BUFFER = "SendBuffer" local RECIEVE_BUFFER = "RecieveBuffer" +local SEND_POSITION = "SendOffset" local BYTE = 1 local SHORT = 2 @@ -178,7 +179,10 @@ for Index, Size in Numbers do return `{Variable} = buffer.read{Index}({RECIEVE_BUFFER}, Read({Size}))` end, Write = function(Value: string): string - return `buffer.write{Index}({SEND_BUFFER}, Allocate({Size}), {Value})` + local Write = Builder.new() + Write.Push(`Allocate({Size})`) + Write.Push(`buffer.write{Index}({SEND_BUFFER}, {SEND_POSITION}, {Value})`, 0) + return Write.Dump() end } Primitives[Index] = { @@ -243,7 +247,10 @@ do return `{Variable} = (buffer.readu8({RECIEVE_BUFFER}, Read({BYTE})) == 1)` end, Write = function(Value: string): string - return `buffer.writeu8({SEND_BUFFER}, Allocate({BYTE}), {Value} and 1 or 0)` + local Write = Builder.new() + Write.Push(`Allocate({BYTE})`) + Write.Push(`buffer.writeu8({SEND_BUFFER}, {SEND_POSITION}, {Value} and 1 or 0)`, 0) + return Write.Dump() end } @@ -265,11 +272,19 @@ do return `{Types.u16.Read("local Length")}\n{Variable} = buffer.readstring({RECIEVE_BUFFER}, Read(Length), Length)` end, Write = function(Value: string, Range: NumberRange?): string + local Write = Builder.new() + if Range and Range.Min == Range.Max then - return `buffer.writestring({SEND_BUFFER}, Allocate({Range.Min}), {Value}, {Range.Min})` + Write.Push(`Allocate({Range.Min})`) + Write.Push(`buffer.writestring({SEND_BUFFER}, {SEND_POSITION}, {Value}, {Range.Min})`, 0) + else + Write.Push(`local Length = #{Value}`) + Write.Push(Types.u16.Write("Length")) + Write.Push(`Allocate(Length)`) + Write.Push(`buffer.writestring({SEND_BUFFER}, {SEND_POSITION}, {Value}, Length)`, 0) end - return `local Length = #{Value}\n{Types.u16.Write("Length")}\nbuffer.writestring({SEND_BUFFER}, Allocate(Length), {Value}, Length)` + return Write.Dump() end } @@ -286,7 +301,13 @@ do return `{Variable} = Vector3.new(buffer.readf32({RECIEVE_BUFFER}, Read({FLOAT})), buffer.readf32({RECIEVE_BUFFER}, Read({FLOAT})), buffer.readf32({RECIEVE_BUFFER}, Read({FLOAT})))` end, Write = function(Value: string) - return `local Vector = {Value}\nbuffer.writef32({SEND_BUFFER}, Allocate({FLOAT}), Vector.X)\nbuffer.writef32({SEND_BUFFER}, Allocate({FLOAT}), Vector.Y)\nbuffer.writef32({SEND_BUFFER}, Allocate({FLOAT}), Vector.Z)` + local Write = Builder.new() + Write.Push(`Allocate({FLOAT * 3})`) + Write.Push(`local Vector = {Value}`) + Write.Push(`buffer.writef32({SEND_BUFFER}, {SEND_POSITION}, Vector.X)`) + Write.Push(`buffer.writef32({SEND_BUFFER}, {SEND_POSITION} + {FLOAT}, Vector.Y)`) + Write.Push(`buffer.writef32({SEND_BUFFER}, {SEND_POSITION} + {FLOAT * 2}, Vector.Z)`, 0) + return Write.Dump() end } @@ -322,7 +343,8 @@ do Write.Push(`{Types.u16.Write("Length")}`, 1, 1) end - Write.Push(`buffer.copy({SEND_BUFFER}, Allocate(Length), {Value}, 0, Length)`, 0, 1) + Write.Push(`Allocate(Length)`, 0, 1) + Write.Push(`buffer.copy({SEND_BUFFER}, {SEND_POSITION}, {Value}, 0, Length)`, 0, 1) return Write.Dump() end @@ -370,7 +392,13 @@ do return `{Variable} = Color3.new(buffer.readu8({RECIEVE_BUFFER}, Read({BYTE})), buffer.readu8({RECIEVE_BUFFER}, Read({BYTE})), buffer.readu8({RECIEVE_BUFFER}, Read({BYTE})))` end, Write = function(Value: string) - return `local Color = {Value}\nbuffer.writeu8({SEND_BUFFER}, Allocate({BYTE}), Color.R * 8)\nbuffer.writeu8({SEND_BUFFER}, Allocate({BYTE}), Color.G * 8)\nbuffer.writeu8({SEND_BUFFER}, Allocate({BYTE}), Color.B * 8)` + local Write = Builder.new() + Write.Push(`Allocate({FLOAT * 3})`) + Write.Push(`local Color = {Value}`) + Write.Push(`buffer.writef32({SEND_BUFFER}, {SEND_POSITION}, Color.R * 8)`) + Write.Push(`buffer.writef32({SEND_BUFFER}, {SEND_POSITION} + {FLOAT}, Color.G * 8)`) + Write.Push(`buffer.writef32({SEND_BUFFER}, {SEND_POSITION} + {FLOAT * 2}, Color.B * 8)`, 0) + return Write.Dump() end } diff --git a/src/Generator/init.luau b/src/Generator/init.luau index 70316b1..196b0a1 100644 --- a/src/Generator/init.luau +++ b/src/Generator/init.luau @@ -31,7 +31,8 @@ local SAVE_BODY = { Header = "local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances", Body = { "Load(PlayersMap[Player])", - "buffer.copy(SendBuffer, Allocate(Size), Buffer, 0, Size)", + "local Position = Allocate(Size)", + "buffer.copy(SendBuffer, Position, Buffer, 0, Size)", "table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)", "PlayersMap[Player] = Save()" } @@ -139,7 +140,7 @@ function Writers.Optional(ReadBody: string, WriteBody: string, Variable: string, Read.PushMultiline(ReadBody, 0, 1) Read.Push("end", 1, 1) - Write.Push(Prefabs.Types.u8.Write(`({Variable} ~= nil) and 1 or 0`), 1, 1) + Write.PushMultiline(Prefabs.Types.u8.Write(`({Variable} ~= nil) and 1 or 0`), 0, 1) Write.Push(`if {Variable} ~= nil then`, 1, 1) Write.PushMultiline(WriteBody, 0, 1) Write.Push("end", 1, 1) @@ -339,13 +340,13 @@ local function WriteBody(Identifier: string, Data: Parser.Declaration, Index: nu local Type, Reference local DataValue = Data.Value - Write.Push(TypePrefabs.u8.Write(Index), 1, 1) + Write.PushMultiline(TypePrefabs.u8.Write(Index), 0, 1) if Invoking ~= nil then - Write.Push(TypePrefabs.u8.Write("InvocationIdentifier"), 1, 1) + Write.PushMultiline(TypePrefabs.u8.Write("InvocationIdentifier"), 0, 1) if not Invoking then - Write.Push(TypePrefabs.u8.Write("1"), 1, 1) + Write.PushMultiline(TypePrefabs.u8.Write("1"), 0, 1) Read.Push(`local {TypePrefabs.u8.Read("Success")} == 1`, 1, 1) Read.Push(`if not Success then`, 1, 1) @@ -586,9 +587,9 @@ function Writers.FunctionDeclaration( Function.Push(`Load(PlayersMap[Player])`, 1, 4) Function.Push(`if not Success then`, 1, 4) - Function.Push(`buffer.writeu8(SendBuffer, Allocate(1), {FunctionIndex})`, 1, 5) - Function.Push(`buffer.writeu8(SendBuffer, Allocate(1), InvocationIdentifier)`, 1, 5) - Function.Push(`buffer.writeu8(SendBuffer, Allocate(1), 0)`, 1, 5) + Function.PushMultiline(TypePrefabs.u8.Write(FunctionIndex), 0, 5) + Function.PushMultiline(TypePrefabs.u8.Write("InvocationIdentifier"), 0, 5) + Function.PushMultiline(TypePrefabs.u8.Write(0), 0, 5) Function.Push(`warn(\`"{Value.Identifier}" encountered an error, \{Return\}\`)`, 1, 5) Function.Push(`else`, 1, 4) Function.Push(`{GetTypesPath(ReturnIdentifier, Scope, true)}(Return, InvocationIdentifier)`, 1, 5) @@ -607,7 +608,7 @@ function Writers.FunctionDeclaration( Function.Push(`end`, 1, 2) ReliableEvents.Push(`{Channel.Listening and "elseif" or "if"} Index == {FunctionIndex} then`, 1, 2) - ReliableEvents.Push(`local {TypePrefabs.u8.Read("InvocationIdentifier")}`, 1, 3) + ReliableEvents.PushMultiline(`local {TypePrefabs.u8.Read("InvocationIdentifier")}`, 0, 3) ReliableEvents.Push(`local Value = {GetTypesPath(DataIdentifier, Scope, false)}()`, 1, 3) ReliableEvents.Push(`if {Events} then`, 1, 3) ReliableEvents.Push(`{Events}(Player, Value, InvocationIdentifier)`, 1, 4) @@ -641,7 +642,7 @@ function Writers.FunctionDeclaration( Function.Push(`end`, 1, 2) ReliableEvents.Push(`{Channel.Listening and "elseif" or "if"} Index == {FunctionIndex} then`, 1, 2) - ReliableEvents.Push(`local {TypePrefabs.u8.Read("InvocationIdentifier")}`, 1, 3) + ReliableEvents.PushMultiline(`local {TypePrefabs.u8.Read("InvocationIdentifier")}`, 0, 3) ReliableEvents.Push(`if Calls[InvocationIdentifier] then`, 1, 3) ReliableEvents.Push(`local Success, Value = pcall(function() return {GetTypesPath(ReturnIdentifier, Scope, false)}() end)`, 1, 4) ReliableEvents.Push(`task.spawn(Calls[InvocationIdentifier], Success, Value)`, 1, 4) diff --git a/src/Templates/Base.txt b/src/Templates/Base.txt index 6a54b8e..9597cb1 100644 --- a/src/Templates/Base.txt +++ b/src/Templates/Base.txt @@ -1,5 +1,6 @@ local Invocations = 0 +local SendOffset = 0 local SendCursor = 0 local SendBuffer = buffer.create(64) local SendInstances = {} @@ -29,12 +30,14 @@ end local function Load(Save: BufferSave?) if Save then SendCursor = Save.Cursor + SendOffset = Save.Cursor SendBuffer = Save.Buffer SendInstances = Save.Instances return end SendCursor = 0 + SendOffset = 0 SendBuffer = buffer.create(64) SendInstances = {} end @@ -66,10 +69,10 @@ local function Allocate(Bytes: number) SendBuffer = Buffer end - local Offset = SendCursor + SendOffset = SendCursor SendCursor += Bytes - return Offset + return SendOffset end local Types = {} diff --git a/src/Templates/Client.txt b/src/Templates/Client.txt index e60e1de..a45ddbe 100644 --- a/src/Templates/Client.txt +++ b/src/Templates/Client.txt @@ -19,6 +19,7 @@ local function StepReplication() Reliable:FireServer(Buffer, SendInstances) SendCursor = 0 + SendOffset = 0 buffer.fill(SendBuffer, 0, 0) table.clear(SendInstances) end diff --git a/test/Client.luau b/test/Client.luau new file mode 100644 index 0000000..854cefe --- /dev/null +++ b/test/Client.luau @@ -0,0 +1,46 @@ +local Shared = require("Shared") + +local Signal = Shared.Signal +local ClientEnviornment = Shared.GetEnviornment() + +local function Clone(Source: buffer): buffer + local Size = buffer.len(Source) + local Target = buffer.create(Size) + buffer.copy(Target, 0, Source, 0, Size) + return Target +end + +local function Fire(Reliable: boolean, ...) + local Arguments = {...} + local Buffer = #Arguments == 2 and Arguments[1] or Arguments[2] + local Instances = Arguments[#Arguments] + + Buffer = Clone(Buffer) + Instances = table.clone(Instances) + + Shared.Bridge:Fire("Client", Reliable and "BLINK_RELIABLE_REMOTE" or "BLINK_UNRELIABLE_REMOTE", Buffer, Instances) +end + +ClientEnviornment.Instances.BLINK_RELIABLE_REMOTE = { + FireServer = function(self, ...) + Fire(true, ...) + end, + OnClientEvent = Signal.new() +} + +ClientEnviornment.Instances.BLINK_UNRELIABLE_REMOTE = { + FireServer = function(self, ...) + Fire(false, ...) + end, + OnClientEvent = Signal.new() +} + +Shared.Bridge:Connect(function(From: string, Remote: string, Buffer: buffer, Instances: {Instance}) + if From == "Client" then + return + end + + ClientEnviornment.Instances[Remote].OnClientEvent:Fire(Buffer, Instances) +end) + +return ClientEnviornment \ No newline at end of file diff --git a/test/Server.luau b/test/Server.luau new file mode 100644 index 0000000..12f8a52 --- /dev/null +++ b/test/Server.luau @@ -0,0 +1,52 @@ +local Shared = require("Shared") + +local Signal = Shared.Signal +local ServerEnviornment = Shared.GetEnviornment() + +local function Clone(Source: buffer): buffer + local Size = buffer.len(Source) + local Target = buffer.create(Size) + buffer.copy(Target, 0, Source, 0, Size) + return Target +end + +local function Fire(Reliable: boolean, ...) + local Arguments = {...} + local Buffer = #Arguments == 2 and Arguments[1] or Arguments[2] + local Instances = Arguments[#Arguments] + + Buffer = Clone(Buffer) + Instances = table.clone(Instances) + + Shared.Bridge:Fire("Server", Reliable and "BLINK_RELIABLE_REMOTE" or "BLINK_UNRELIABLE_REMOTE", Buffer, Instances) +end + +ServerEnviornment.Instances.BLINK_RELIABLE_REMOTE = { + FireClient = function(self, Player, ...) + Fire(true, ...) + end, + FireAllClients = function(self, ...) + Fire(true, ...) + end, + OnServerEvent = Signal.new() +} + +ServerEnviornment.Instances.BLINK_UNRELIABLE_REMOTE = { + FireClient = function(Player, ...) + Fire(false, ...) + end, + FireAllClients = function(self, ...) + Fire(false, ...) + end, + OnServerEvent = Signal.new() +} + +Shared.Bridge:Connect(function(From: string, Remote: string, Buffer: buffer, Instances: {Instance}) + if From == "Server" then + return + end + + ServerEnviornment.Instances[Remote].OnServerEvent:Fire(Shared.Player, Buffer, Instances) +end) + +return ServerEnviornment \ No newline at end of file diff --git a/test/Sources/Test.txt b/test/Sources/Test.txt index 97645ee..83258be 100644 --- a/test/Sources/Test.txt +++ b/test/Sources/Test.txt @@ -67,42 +67,42 @@ event UnreliableClient = { event InstanceAny = { From = Server, - Type = Unreliable, + Type = Reliable, Call = SingleSync, Data = Instance } event InstanceOfType = { From = Server, - Type = Unreliable, + Type = Reliable, Call = SingleSync, Data = Instance(Sound) } event InstanceOptional = { From = Server, - Type = Unreliable, + Type = Reliable, Call = SingleSync, Data = Instance? } event Reference = { From = Server, - Type = Unreliable, + Type = Reliable, Call = SingleSync, Data = Example? } event ReferenceArray = { From = Server, - Type = Unreliable, + Type = Reliable, Call = SingleSync, Data = Example[8] } event ReferenceOptional = { From = Server, - Type = Unreliable, + Type = Reliable, Call = SingleSync, Data = Example? } diff --git a/test/Test.luau b/test/Test.luau index 8181c01..3d81939 100644 --- a/test/Test.luau +++ b/test/Test.luau @@ -22,13 +22,13 @@ end PrettyPrint("blue", "Compiling source files...") for _, File in Sources do local AbsolutePath = `C:/Users/User/Documents/GitHub/Blink/test/Sources/{File}` - --[[PrettyPrint("cyan", `Compiling: {File}...`, true) + PrettyPrint("cyan", `Compiling: {File}...`, true) local Result = process.spawn("lune", {"run", Compiler, AbsolutePath}, {cwd = "C:/Users/User/Documents/GitHub/Blink/src/"}) if not Result.ok then ErrorPrint(`{File} failed to compile!\n{Result.stderr}`) else OkPrint(`{File} compiled successfully.`) - end]] + end end PrettyPrint("green", `Successfully compiled {#Sources}/{#Sources} source files!`) @@ -207,18 +207,21 @@ local function QueueAndExpect(Name: string, OutgoingEvent: any, IncomingEvent: a OkPrint(`Test "{Name}" passed!`) end -local function FireAndExpectAll(Name: string, OutgoingEvent: any, IncomingEvent: any, ...: any) +local function FireAndExpectAll(Name: string, OutgoingEvent: any, IncomingEvent: any, Values: {any}) PrettyPrint("cyan", `Testing: {Name}...`, true) - local Values = {...} + local Index = 0 local Results: {unknown} = {} local Disconnect = IncomingEvent.On(function(First, Second) - table.insert(Results, if Second ~= nil then Second else First) + local Value = (if Second ~= nil then Second else First) + + Index += 1 + Results[Index] = Value end) local Method = OutgoingEvent.FireAll or OutgoingEvent.Fire - for _, Value in Values do - Method(Value) + for Index = 1, table.maxn(Values) do + Method(Values[Index]) end --> Wait for replication @@ -249,6 +252,7 @@ local Instance = setmetatable({ local InstanceNeverIsA = setmetatable({ __typeof = "Instance", + ClassName = "Never", IsA = function(self) return false end @@ -294,14 +298,14 @@ FireAndExpect("Referency Optional (nil)", Server.ReferenceOptional, Client.Refer InvokeAndExpect("Remote Function", Client.RemoteFunction, Server.RemoteFunction, 1, true) InvokeAndExpect("Remote Function (error)", Client.RemoteFunction, Server.RemoteFunction, 1, false) -QueueAndExpect("Reliable Queue", Server.ReliableServer, Client.ReliableClient, 1) -QueueAndExpect("Unreliable Queue", Server.UnreliableServer, Client.UnreliableClient, 1) +QueueAndExpect("Reliable Queue", Server.ReliableServer, Client.ReliableServer, 1) +QueueAndExpect("Unreliable Queue", Server.UnreliableServer, Client.UnreliableServer, 1) FireAndFail("Invalid Instance Any", Server.InstanceAny, Client.InstanceAny, true) FireAndFail("Invalid Instance Type", Server.InstanceOfType, Client.InstanceOfType, InstanceNeverIsA) -FireAndExpectAll("Correct order", Server.ReliableServer, Client.ReliableServer, 1, 2, 3, 4) -FireAndExpectAll("Multiple instances (nil and non nil)", Server.InstanceOptional, Client.InstanceOptional, false, Instance, false, Instance, Instance) +FireAndExpectAll("Correct order", Server.ReliableServer, Client.ReliableServer, {1, 2, 3, 4}) +FireAndExpectAll("Multiple instances (nil and non nil)", Server.InstanceOptional, Client.InstanceOptional, {nil, Instance, nil, Instance, Instance}) PrettyPrint("green", `All tests passed successfully!`)