Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
0.5.3
Browse files Browse the repository at this point in the history
  • Loading branch information
ffrostfall committed Jul 31, 2023
1 parent a5ce9b1 commit b791b3a
Show file tree
Hide file tree
Showing 19 changed files with 304 additions and 75 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

This project uses [semantic versioning](https://semver.org/spec/v2.0.0.html).

## [version 0.5.3](incomplete): 7/31/2023

## Added
- A mock API for when BridgeNet2 is ran in edit mode. Limitations: InvokeServerAsync will infinitely yield, connections will never run.

### Improvements
- BridgeNet2 nows prints the current version upon being loaded
- Improved output readability
- **Potentially breaking:** Bridges now are cached- this means you will have the same bridge object across scripts. This should be more expected behavior, and should overall be an improvement.

### Fixes
- Potentially fixed some issues with loading and identifiers?
- Fixed a bug where referencing a bridge multiple times clearing connections each time (Except on the client this time)

## [version 0.5.2](https://github.com/ffrostflame/BridgeNet2/releases/tag/v0.5.2): 7/15/2023

### Improvements
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img src=".moonwave/static/logo.png" width="256" />
</div>

# BridgeNet2 v0.5.2
# BridgeNet2 v0.5.3

## Blazing fast & opinionated networking library designed to reduce bandwidth.

Expand Down
2 changes: 1 addition & 1 deletion sourcemap.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"name":"bridgenet2-test","className":"DataModel","filePaths":["testing.project.json"],"children":[{"name":"ReplicatedStorage","className":"ReplicatedStorage","children":[{"name":"Packages","className":"Folder","children":[{"name":"RemotePacketSizeCounter","className":"ModuleScript","filePaths":["Packages\\RemotePacketSizeCounter.lua"]},{"name":"TableKit","className":"ModuleScript","filePaths":["Packages\\TableKit.lua"]},{"name":"_Index","className":"Folder","children":[{"name":"[email protected]","className":"Folder","children":[{"name":"tablekit","className":"ModuleScript","filePaths":["Packages\\_Index\\[email protected]\\tablekit\\src\\init.luau","Packages\\_Index\\[email protected]\\tablekit\\default.project.json"]}]},{"name":"[email protected]","className":"Folder","children":[{"name":"remotepacketsizecounter","className":"ModuleScript","filePaths":["Packages\\_Index\\[email protected]\\remotepacketsizecounter\\src\\init.luau","Packages\\_Index\\[email protected]\\remotepacketsizecounter\\default.project.json"]}]}]},{"name":"bridgenet2","className":"ModuleScript","filePaths":["src\\init.luau"],"children":[{"name":"Client","className":"ModuleScript","filePaths":["src\\Client\\init.luau"],"children":[{"name":"ClientBridge","className":"ModuleScript","filePaths":["src\\Client\\ClientBridge.luau"]},{"name":"ClientConnection","className":"ModuleScript","filePaths":["src\\Client\\ClientConnection.luau"]},{"name":"ClientIdentifiers","className":"ModuleScript","filePaths":["src\\Client\\ClientIdentifiers.luau"]},{"name":"ClientProcess","className":"ModuleScript","filePaths":["src\\Client\\ClientProcess.luau"]}]},{"name":"Constants","className":"ModuleScript","filePaths":["src\\Constants.luau"]},{"name":"PublicTypes","className":"ModuleScript","filePaths":["src\\PublicTypes.luau"]},{"name":"Server","className":"ModuleScript","filePaths":["src\\Server\\init.luau"],"children":[{"name":"HandleInvalidPlayer","className":"ModuleScript","filePaths":["src\\Server\\HandleInvalidPlayer.luau"]},{"name":"PlayerContainers","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\init.luau"],"children":[{"name":"All","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\All.luau"]},{"name":"Except","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\Except.luau"]},{"name":"Players","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\Players.luau"]},{"name":"Single","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\Single.luau"]}]},{"name":"ServerBridge","className":"ModuleScript","filePaths":["src\\Server\\ServerBridge.luau"]},{"name":"ServerConnection","className":"ModuleScript","filePaths":["src\\Server\\ServerConnection.luau"]},{"name":"ServerIdentifiers","className":"ModuleScript","filePaths":["src\\Server\\ServerIdentifiers.luau"]},{"name":"ServerProcess","className":"ModuleScript","filePaths":["src\\Server\\ServerProcess.luau"]}]},{"name":"Types","className":"ModuleScript","filePaths":["src\\Types.luau"]},{"name":"Utilities","className":"Folder","children":[{"name":"NetworkUtils","className":"ModuleScript","filePaths":["src\\Utilities\\NetworkUtils.luau"]},{"name":"Output","className":"ModuleScript","filePaths":["src\\Utilities\\Output.luau"]},{"name":"RecycledSpawn","className":"ModuleScript","filePaths":["src\\Utilities\\RecycledSpawn.luau"]},{"name":"tostringData","className":"ModuleScript","filePaths":["src\\Utilities\\tostringData.luau"]}]}]}]},{"name":"benches","className":"Folder","children":[{"name":"Hex.bench","className":"ModuleScript","filePaths":["benchmarks\\Hex.bench.luau"]}]},{"name":"framework","className":"ModuleScript","filePaths":["testing/framework\\init.luau"],"children":[{"name":"bootstrapper","className":"ModuleScript","filePaths":["testing/framework\\bootstrapper.luau"]},{"name":"expect","className":"ModuleScript","filePaths":["testing/framework\\expect.luau"]}]}]},{"name":"ServerScriptService","className":"ServerScriptService","children":[{"name":"featureTests","className":"Script","filePaths":["testing/tests/server\\featureTests\\init.server.luau"]}]},{"name":"StarterPlayer","className":"StarterPlayer","children":[{"name":"StarterPlayerScripts","className":"StarterPlayerScripts","children":[{"name":"featureTests","className":"LocalScript","filePaths":["testing/tests/client\\featureTests\\init.client.luau"]}]}]}]}
{"name":"bridgenet2-test","className":"DataModel","filePaths":["testing.project.json"],"children":[{"name":"ReplicatedStorage","className":"ReplicatedStorage","children":[{"name":"Packages","className":"Folder","children":[{"name":"RemotePacketSizeCounter","className":"ModuleScript","filePaths":["Packages\\RemotePacketSizeCounter.lua"]},{"name":"TableKit","className":"ModuleScript","filePaths":["Packages\\TableKit.lua"]},{"name":"_Index","className":"Folder","children":[{"name":"[email protected]","className":"Folder","children":[{"name":"tablekit","className":"ModuleScript","filePaths":["Packages\\_Index\\[email protected]\\tablekit\\src\\init.luau","Packages\\_Index\\[email protected]\\tablekit\\default.project.json"]}]},{"name":"[email protected]","className":"Folder","children":[{"name":"remotepacketsizecounter","className":"ModuleScript","filePaths":["Packages\\_Index\\[email protected]\\remotepacketsizecounter\\src\\init.luau","Packages\\_Index\\[email protected]\\remotepacketsizecounter\\default.project.json"]}]}]},{"name":"bridgenet2","className":"ModuleScript","filePaths":["src\\init.luau"],"children":[{"name":"Client","className":"ModuleScript","filePaths":["src\\Client\\init.luau"],"children":[{"name":"ClientBridge","className":"ModuleScript","filePaths":["src\\Client\\ClientBridge.luau"]},{"name":"ClientConnection","className":"ModuleScript","filePaths":["src\\Client\\ClientConnection.luau"]},{"name":"ClientIdentifiers","className":"ModuleScript","filePaths":["src\\Client\\ClientIdentifiers.luau"]},{"name":"ClientProcess","className":"ModuleScript","filePaths":["src\\Client\\ClientProcess.luau"]}]},{"name":"Constants","className":"ModuleScript","filePaths":["src\\Constants.luau"]},{"name":"PublicTypes","className":"ModuleScript","filePaths":["src\\PublicTypes.luau"]},{"name":"Server","className":"ModuleScript","filePaths":["src\\Server\\init.luau"],"children":[{"name":"HandleInvalidPlayer","className":"ModuleScript","filePaths":["src\\Server\\HandleInvalidPlayer.luau"]},{"name":"PlayerContainers","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\init.luau"],"children":[{"name":"All","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\All.luau"]},{"name":"Except","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\Except.luau"]},{"name":"Players","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\Players.luau"]},{"name":"Single","className":"ModuleScript","filePaths":["src\\Server\\PlayerContainers\\Single.luau"]}]},{"name":"ServerBridge","className":"ModuleScript","filePaths":["src\\Server\\ServerBridge.luau"]},{"name":"ServerConnection","className":"ModuleScript","filePaths":["src\\Server\\ServerConnection.luau"]},{"name":"ServerIdentifiers","className":"ModuleScript","filePaths":["src\\Server\\ServerIdentifiers.luau"]},{"name":"ServerProcess","className":"ModuleScript","filePaths":["src\\Server\\ServerProcess.luau"]}]},{"name":"Studio","className":"Folder","children":[{"name":"MockBridge","className":"ModuleScript","filePaths":["src\\Studio\\MockBridge.luau"]},{"name":"MockConnection","className":"ModuleScript","filePaths":["src\\Studio\\MockConnection.luau"]},{"name":"MockIdentifiers","className":"ModuleScript","filePaths":["src\\Studio\\MockIdentifiers.luau"]}]},{"name":"Types","className":"ModuleScript","filePaths":["src\\Types.luau"]},{"name":"Utilities","className":"Folder","children":[{"name":"NetworkUtils","className":"ModuleScript","filePaths":["src\\Utilities\\NetworkUtils.luau"]},{"name":"Output","className":"ModuleScript","filePaths":["src\\Utilities\\Output.luau"]},{"name":"RecycledSpawn","className":"ModuleScript","filePaths":["src\\Utilities\\RecycledSpawn.luau"]},{"name":"isEditMode","className":"ModuleScript","filePaths":["src\\Utilities\\isEditMode.luau"]},{"name":"tostringData","className":"ModuleScript","filePaths":["src\\Utilities\\tostringData.luau"]}]},{"name":"version","className":"ModuleScript","filePaths":["src\\version.luau"]}]}]},{"name":"benches","className":"Folder","children":[{"name":"Hex.bench","className":"ModuleScript","filePaths":["benchmarks\\Hex.bench.luau"]}]},{"name":"framework","className":"ModuleScript","filePaths":["testing/framework\\init.luau"],"children":[{"name":"bootstrapper","className":"ModuleScript","filePaths":["testing/framework\\bootstrapper.luau"]},{"name":"expect","className":"ModuleScript","filePaths":["testing/framework\\expect.luau"]}]}]},{"name":"ServerScriptService","className":"ServerScriptService","children":[{"name":"featureTests","className":"Script","filePaths":["testing/tests/server\\featureTests\\init.server.luau"]}]},{"name":"StarterPlayer","className":"StarterPlayer","children":[{"name":"StarterPlayerScripts","className":"StarterPlayerScripts","children":[{"name":"featureTests","className":"LocalScript","filePaths":["testing/tests/client\\featureTests\\init.client.luau"]}]}]}]}
8 changes: 4 additions & 4 deletions src/Client/ClientBridge.luau
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function clientBridgePrototype:Connect(callback: (content: Types.Content) -> ())

return ClientConnection(self._identifier, function(content)
if typeof(content) == "table" then
if (content :: {})[1] == ClientIdentifiers.ref("REQUEST") then
if (content :: {})[1] == ClientIdentifiers.ref("REQUEST", 3, false) then
return
end
end
Expand Down Expand Up @@ -162,14 +162,14 @@ end

function clientBridgePrototype:InvokeServerAsync(content: any)
Output.fatalAssert(tostring(self) == "ClientBridge", "InvokeServerAsync called with . instead of :")
self:Fire({ ClientIdentifiers.ref("REQUEST"), content })
self:Fire({ ClientIdentifiers.ref("REQUEST", 3, false), content })
local thread = coroutine.running()
local connection
connection = ClientProcess.connect(self._identifier, function(reply)
if typeof(reply) ~= "table" then
return
end
if (reply :: {})[1] == ClientIdentifiers.ref("REQUEST") then
if (reply :: {})[1] == ClientIdentifiers.ref("REQUEST", 3, false) then
connection()
coroutine.resume(thread, (reply :: {})[2])
end
Expand Down Expand Up @@ -200,7 +200,7 @@ return function(name: string)
local self = setmetatable({
Logging = false,

_identifier = ClientIdentifiers.ref(name),
_identifier = ClientIdentifiers.ref(name, 3, true),
_name = name,

_inboundMiddleware = {},
Expand Down
100 changes: 55 additions & 45 deletions src/Client/ClientIdentifiers.luau
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")

local Types = require(script.Parent.Parent.Types)
local Output = require(script.Parent.Parent.Utilities.Output)
Expand All @@ -18,28 +17,21 @@ function ClientIdentifiers.start()

-- Loop through every single existing identifer, stored as an attribute.
-- Simply parse them into the system.
for id, value in identifierStorage:GetAttributes() do
fullIdentifierMap[id] = value
compressedIdentifierMap[value] = id
for identifier, value in identifierStorage:GetAttributes() do
fullIdentifierMap[identifier] = value
compressedIdentifierMap[value] = identifier
ClientIdentifiers.loadIdentifier(identifier, value)
end

identifierStorage.AttributeChanged:Connect(function(id: string)
local packed: string = identifierStorage:GetAttribute(id)

if packed then
-- If there's any threads waiting for an identifer, resume them, then delete the reference entirely.
local waitingThreads = yieldingThreads[id]
if waitingThreads then
for thread in waitingThreads do
task.spawn(thread, packed)
end

yieldingThreads[id] = nil
end

-- Put the identifier into the system.
fullIdentifierMap[id] = packed
compressedIdentifierMap[packed] = id

ClientIdentifiers.loadIdentifier(id, packed)
else
-- The identifier was deleted.
-- TODO why is this here? you can't even delete identifiers atm
Expand All @@ -49,57 +41,75 @@ function ClientIdentifiers.start()
end
end)

ClientIdentifiers.ref("NIL_VALUE")
ClientIdentifiers.ref("NIL_VALUE", 3, false)
ClientIdentifiers.ref("REQUEST", 3, false)
end

function ClientIdentifiers.ref(identifierName: string, maxWaitTime: number?)
Output.typecheck("string", "ReferenceIdentifier", "identifierName", identifierName)
function ClientIdentifiers.loadIdentifier(identifierName: string, value: string)
if not yieldingThreads[identifierName] then
-- There aren't any yielding threads, so we don't need to resume any.
return
end

local indexes = {}

for index, thread in yieldingThreads[identifierName] do
-- Resume yielding threads with the compressed identifier.
-- This will make the function return the compressed identifier
coroutine.resume(thread, value)

if RunService:IsStudio() then
fullIdentifierMap[identifierName] = identifierName
compressedIdentifierMap[identifierName] = identifierName
return identifierName
-- Insert it to say we aren't yielding this thread anymore
table.insert(indexes, index)
end

if maxWaitTime ~= nil then
Output.typecheck("number", "ReferenceIdentifier", "maxWaitTime", maxWaitTime)
-- Since the timeout relies on knowing whether or not the thread is still yielding, we need to remove it.
for _, index in indexes do
table.remove(yieldingThreads[identifierName], index)
end
local maxWaitTimeArg = maxWaitTime or 1
end

function ClientIdentifiers.waitForIdentifier(identifierName: string, timeout: number, bridgeCall: boolean)
timeout = timeout or 1

local identifier = fullIdentifierMap[identifierName]
if identifier then
return identifier
end

local thread = coroutine.running()

local threads = yieldingThreads[identifierName]
if threads then
threads[thread] = true
else
threads = { [thread] = true }
yieldingThreads[identifierName] = threads
if not yieldingThreads[identifierName] then
yieldingThreads[identifierName] = {}
end

-- Simple timeout implementation
local timeOut = task.delay(maxWaitTimeArg, function()
threads[thread] = nil
-- This needs to be a variable because the task.delay will run in a separate thread
local runningThread = coroutine.running()
table.insert(yieldingThreads[identifierName], runningThread)

task.spawn(thread, nil)
task.delay(timeout, function()
-- Are we still yielding?
if table.find(yieldingThreads[identifierName], runningThread) then
Output.fatal(
`reached max wait time for {if bridgeCall then "bridge" else "identifier"} {identifierName}, broke yield. Did you forget to implement it on the server?`
)
end
end)

local retrievedIdentifier = coroutine.yield()
-- cancel the timeout thread
return coroutine.yield()
end

function ClientIdentifiers.ref(identifierName: string, maxWaitTime: number?, bridgeCall: boolean)
Output.typecheck("string", "ReferenceIdentifier", "identifierName", identifierName)

if maxWaitTime ~= nil then
Output.typecheck("number", "ReferenceIdentifier", "maxWaitTime", maxWaitTime)
end
local maxWaitTimeArg = maxWaitTime or 1

if retrievedIdentifier == nil then
Output.fatal(
`reached max wait time for identifier {identifierName}, broke yield. Did you forget to implement it on the server?`
)
else
task.cancel(timeOut)
local identifier = fullIdentifierMap[identifierName]
if identifier then
return identifier
end

return retrievedIdentifier
return ClientIdentifiers.waitForIdentifier(identifierName, maxWaitTimeArg, bridgeCall)
end

function ClientIdentifiers.deser(compressedIdentifier: Types.Identifier): Types.Identifier?
Expand Down
3 changes: 2 additions & 1 deletion src/Client/ClientProcess.luau
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ local RunService = game:GetService("RunService")
local Types = require(script.Parent.Parent.Types)
local Output = require(script.Parent.Parent.Utilities.Output)
local RecycledSpawn = require(script.Parent.Parent.Utilities.RecycledSpawn)
local version = require(script.Parent.Parent.version)

local outboundQueue: { string | any } = {}
local outboundQueueLength: number = 0 -- Cannot use # operator because of potentially nil values
Expand All @@ -15,7 +16,7 @@ local ClientProcess = {}

function ClientProcess.start()
debug.setmemorycategory("BridgeNet2")
Output.log("Loading BridgeNet2")
Output.log(`Loading client version {version}`)

-- :WaitForChild() to confirm instances exist, we're in another thread.
local DataRemoteEvent: RemoteEvent = ReplicatedStorage:WaitForChild("dataRemoteEvent")
Expand Down
31 changes: 29 additions & 2 deletions src/Client/init.luau
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,55 @@ local ClientBridge = require(script.ClientBridge)
local ClientIdentifiers = require(script.ClientIdentifiers)
local ClientProcess = require(script.ClientProcess)
local Types = require(script.Parent.Types)
local isEditMode = require(script.Parent.Utilities.isEditMode)

local activeBridges = {}

local Client = {}

function Client.start()
if isEditMode then
return
end

ClientProcess.start()
ClientIdentifiers.start()
end

function Client.ser(identifierName: Types.Identifier): Types.Identifier?
if isEditMode then
return identifierName
end

return ClientIdentifiers.ser(identifierName)
end

function Client.deser(compressedIdentifier: Types.Identifier): Types.Identifier?
if isEditMode then
return compressedIdentifier
end

return ClientIdentifiers.deser(compressedIdentifier)
end

function Client.makeIdentifier(name: string, timeout: number?)
return ClientIdentifiers.ref(name, timeout)
if isEditMode then
return name
end

return ClientIdentifiers.ref(name, timeout, false)
end

function Client.makeBridge(name: string)
return ClientBridge(name)
if activeBridges[name] then
return activeBridges[name]
else
local bridge = ClientBridge(name)

activeBridges[name] = bridge

return bridge
end
end

return Client
Loading

0 comments on commit b791b3a

Please sign in to comment.