Initial push
Some checks are pending
CI / Analyze (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Styling (push) Waiting to run

This commit is contained in:
marked 2024-11-11 19:35:24 +01:00
parent 5a9167e2f5
commit 8ee75b96ed
51 changed files with 4437 additions and 2 deletions

View file

@ -0,0 +1,15 @@
--!strict
local data = require("@client/data")
local net = require("@net/client")
local function init()
net.replicate_data.on(function(difference)
data.apply_difference(difference)
end)
end
return {
init = init,
-- data is important
priority = math.huge,
}

View file

14
src/client/data.luau Normal file
View file

@ -0,0 +1,14 @@
--!strict
local sapphire_data = require("@pkg/sapphire_data")
export type data = {
coins: number,
}
local template: data = {
coins = 0,
}
return sapphire_data.client({
template = template,
})

View file

@ -0,0 +1,16 @@
--!strict
local sapphire = require("@pkg/sapphire")
local data = require("@client/data")
local ecs = require("@pkg/sapphire_jecs")
local lifecycles = require("@pkg/sapphire_lifecycles")
local controllers = script.Parent.controllers
sapphire()
:register_singletons(controllers)
:register_singletons(controllers.systems)
:use(data)
:use(ecs)
:use(lifecycles)
:start()

10
src/client/player.luau Normal file
View file

@ -0,0 +1,10 @@
--!strict
local Players = game:GetService("Players")
local components = require("@shared/components")
local ecs = require("@pkg/sapphire_jecs")
local player = Players.LocalPlayer
local player_entity = ecs.ref("local-player"):set(components.player, player):id()
return player_entity

265
src/net/client.luau Normal file
View file

@ -0,0 +1,265 @@
--!strict
--!native
--!optimize 2
--!nolint LocalShadow
--#selene: allow(shadowing)
-- File generated by Blink v0.14.15 (https://github.com/1Axen/Blink)
-- This file is not meant to be edited
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Invocations = 0
local SendSize = 64
local SendOffset = 0
local SendCursor = 0
local SendBuffer = buffer.create(64)
local SendInstances = {}
local RecieveCursor = 0
local RecieveBuffer = buffer.create(64)
local RecieveInstances = {}
local RecieveInstanceCursor = 0
type Entry = {
value: any,
next: Entry?
}
type Queue = {
head: Entry?,
tail: Entry?
}
type BufferSave = {
Size: number,
Cursor: number,
Buffer: buffer,
Instances: {Instance}
}
local function Read(Bytes: number)
local Offset = RecieveCursor
RecieveCursor += Bytes
return Offset
end
local function Save(): BufferSave
return {
Size = SendSize,
Cursor = SendCursor,
Buffer = SendBuffer,
Instances = SendInstances
}
end
local function Load(Save: BufferSave?)
if Save then
SendSize = Save.Size
SendCursor = Save.Cursor
SendOffset = Save.Cursor
SendBuffer = Save.Buffer
SendInstances = Save.Instances
return
end
SendSize = 64
SendCursor = 0
SendOffset = 0
SendBuffer = buffer.create(64)
SendInstances = {}
end
local function Invoke()
if Invocations == 255 then
Invocations = 0
end
local Invocation = Invocations
Invocations += 1
return Invocation
end
local function Allocate(Bytes: number)
local InUse = (SendCursor + Bytes)
if InUse > SendSize then
--> Avoid resizing the buffer for every write
while InUse > SendSize do
SendSize *= 1.5
end
local Buffer = buffer.create(SendSize)
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
SendBuffer = Buffer
end
SendOffset = SendCursor
SendCursor += Bytes
return SendOffset
end
local function CreateQueue(): Queue
return {
head = nil,
tail = nil
}
end
local function Pop(queue: Queue): any
local head = queue.head
if head == nil then
return
end
queue.head = head.next
return head.value
end
local function Push(queue: Queue, value: any)
local entry: Entry = {
value = value,
next = nil
}
if queue.tail ~= nil then
queue.tail.next = entry
end
queue.tail = entry
if queue.head == nil then
queue.head = entry
end
end
local Types = {}
local Calls = table.create(256)
local Events: any = {
Reliable = table.create(256),
Unreliable = table.create(256)
}
local Queue: any = {
Reliable = table.create(256),
Unreliable = table.create(256)
}
Queue.Reliable[0] = table.create(256)
function Types.ReadEVENT_replicate_data(): (buffer)
-- Read BLOCK: 2 bytes
local BLOCK_START = Read(2)
local Length = buffer.readu16(RecieveBuffer, BLOCK_START + 0)
local Value = buffer.create(Length)
buffer.copy(Value, 0, RecieveBuffer, Read(Length), Length)
return Value
end
function Types.WriteEVENT_replicate_data(Value: buffer): ()
-- Allocate BLOCK: 3 bytes
local BLOCK_START = Allocate(3)
buffer.writeu8(SendBuffer, BLOCK_START + 0, 0)
local Length = buffer.len(Value)
buffer.writeu16(SendBuffer, BLOCK_START + 1, Length)
Allocate(Length)
buffer.copy(SendBuffer, SendOffset, Value, 0, Length)
end
if not RunService:IsRunning() then
local NOOP = function() end
local Returns = table.freeze({
replicate_data = {
on = NOOP
},
})
return Returns :: BLINK_EVENTS_SYMBOL
end
if not RunService:IsClient() then
error("Client network module can only be required from the client.")
end
local Reliable: RemoteEvent = ReplicatedStorage:WaitForChild("BLINK_RELIABLE_REMOTE") :: RemoteEvent
local Unreliable: UnreliableRemoteEvent = ReplicatedStorage:WaitForChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
local function StepReplication()
if SendCursor <= 0 then
return
end
local Buffer = buffer.create(SendCursor)
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
Reliable:FireServer(Buffer, SendInstances)
SendSize = 64
SendCursor = 0
SendOffset = 0
SendBuffer = buffer.create(64)
table.clear(SendInstances)
end
local Elapsed = 0
RunService.Heartbeat:Connect(function(DeltaTime: number)
Elapsed += DeltaTime
if Elapsed >= (1 / 61) then
Elapsed -= (1 / 61)
StepReplication()
end
end)
Reliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: {Instance})
RecieveCursor = 0
RecieveBuffer = Buffer
RecieveInstances = Instances
RecieveInstanceCursor = 0
local Size = buffer.len(RecieveBuffer)
while (RecieveCursor < Size) do
-- Read BLOCK: 1 bytes
local BLOCK_START = Read(1)
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
if Index == 0 then
local Value: buffer = Types.ReadEVENT_replicate_data()
if Events.Reliable[0] ~= nil then
Events.Reliable[0](Value)
else
if #Queue.Reliable[0] > 256 then
warn("[Blink]: Event queue of \"replicate_data\" exceeded 256, did you forget to implement a listener?")
end
table.insert(Queue.Reliable[0], {Value} :: {any})
end
end
end
end)
Unreliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: {Instance})
RecieveCursor = 0
RecieveBuffer = Buffer
RecieveInstances = Instances
RecieveInstanceCursor = 0
local Size = buffer.len(RecieveBuffer)
while (RecieveCursor < Size) do
-- Read BLOCK: 1 bytes
local BLOCK_START = Read(1)
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
end
end)
local Returns = table.freeze({
step_replication = StepReplication,
replicate_data = {
on = function(Listener: (Value: buffer) -> ()): () -> ()
Events.Reliable[0] = Listener
for Index, Arguments in Queue.Reliable[0] do
Listener(table.unpack(Arguments))
end
Queue.Reliable[0] = {}
return function (): ()
Events.Reliable[0] = nil
end
end
},
})
type BLINK_EVENTS_SYMBOL = typeof(Returns)
return Returns :: BLINK_EVENTS_SYMBOL

304
src/net/server.luau Normal file
View file

@ -0,0 +1,304 @@
--!strict
--!native
--!optimize 2
--!nolint LocalShadow
--#selene: allow(shadowing)
-- File generated by Blink v0.14.15 (https://github.com/1Axen/Blink)
-- This file is not meant to be edited
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Invocations = 0
local SendSize = 64
local SendOffset = 0
local SendCursor = 0
local SendBuffer = buffer.create(64)
local SendInstances = {}
local RecieveCursor = 0
local RecieveBuffer = buffer.create(64)
local RecieveInstances = {}
local RecieveInstanceCursor = 0
type Entry = {
value: any,
next: Entry?
}
type Queue = {
head: Entry?,
tail: Entry?
}
type BufferSave = {
Size: number,
Cursor: number,
Buffer: buffer,
Instances: {Instance}
}
local function Read(Bytes: number)
local Offset = RecieveCursor
RecieveCursor += Bytes
return Offset
end
local function Save(): BufferSave
return {
Size = SendSize,
Cursor = SendCursor,
Buffer = SendBuffer,
Instances = SendInstances
}
end
local function Load(Save: BufferSave?)
if Save then
SendSize = Save.Size
SendCursor = Save.Cursor
SendOffset = Save.Cursor
SendBuffer = Save.Buffer
SendInstances = Save.Instances
return
end
SendSize = 64
SendCursor = 0
SendOffset = 0
SendBuffer = buffer.create(64)
SendInstances = {}
end
local function Invoke()
if Invocations == 255 then
Invocations = 0
end
local Invocation = Invocations
Invocations += 1
return Invocation
end
local function Allocate(Bytes: number)
local InUse = (SendCursor + Bytes)
if InUse > SendSize then
--> Avoid resizing the buffer for every write
while InUse > SendSize do
SendSize *= 1.5
end
local Buffer = buffer.create(SendSize)
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
SendBuffer = Buffer
end
SendOffset = SendCursor
SendCursor += Bytes
return SendOffset
end
local function CreateQueue(): Queue
return {
head = nil,
tail = nil
}
end
local function Pop(queue: Queue): any
local head = queue.head
if head == nil then
return
end
queue.head = head.next
return head.value
end
local function Push(queue: Queue, value: any)
local entry: Entry = {
value = value,
next = nil
}
if queue.tail ~= nil then
queue.tail.next = entry
end
queue.tail = entry
if queue.head == nil then
queue.head = entry
end
end
local Types = {}
local Calls = table.create(256)
local Events: any = {
Reliable = table.create(256),
Unreliable = table.create(256)
}
local Queue: any = {
Reliable = table.create(256),
Unreliable = table.create(256)
}
function Types.ReadEVENT_replicate_data(): (buffer)
-- Read BLOCK: 2 bytes
local BLOCK_START = Read(2)
local Length = buffer.readu16(RecieveBuffer, BLOCK_START + 0)
local Value = buffer.create(Length)
buffer.copy(Value, 0, RecieveBuffer, Read(Length), Length)
return Value
end
function Types.WriteEVENT_replicate_data(Value: buffer): ()
-- Allocate BLOCK: 3 bytes
local BLOCK_START = Allocate(3)
buffer.writeu8(SendBuffer, BLOCK_START + 0, 0)
local Length = buffer.len(Value)
buffer.writeu16(SendBuffer, BLOCK_START + 1, Length)
Allocate(Length)
buffer.copy(SendBuffer, SendOffset, Value, 0, Length)
end
if not RunService:IsRunning() then
local NOOP = function() end
local Returns = table.freeze({
replicate_data = {
fire = NOOP,
fire_all = NOOP,
fire_list = NOOP,
fire_except = NOOP
},
})
return Returns :: BLINK_EVENTS_SYMBOL
end
if not RunService:IsServer() then
error("Server network module can only be required from the server.")
end
local Reliable: RemoteEvent = ReplicatedStorage:FindFirstChild("BLINK_RELIABLE_REMOTE") :: RemoteEvent
if not Reliable then
local RemoteEvent = Instance.new("RemoteEvent")
RemoteEvent.Name = "BLINK_RELIABLE_REMOTE"
RemoteEvent.Parent = ReplicatedStorage
Reliable = RemoteEvent
end
local Unreliable: UnreliableRemoteEvent = ReplicatedStorage:FindFirstChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
if not Unreliable then
local UnreliableRemoteEvent = Instance.new("UnreliableRemoteEvent")
UnreliableRemoteEvent.Name = "BLINK_UNRELIABLE_REMOTE"
UnreliableRemoteEvent.Parent = ReplicatedStorage
Unreliable = UnreliableRemoteEvent
end
local PlayersMap: {[Player]: BufferSave} = {}
Players.PlayerRemoving:Connect(function(Player)
PlayersMap[Player] = nil
end)
local function StepReplication()
for Player, Send in PlayersMap do
if Send.Cursor <= 0 then
continue
end
local Buffer = buffer.create(Send.Cursor)
buffer.copy(Buffer, 0, Send.Buffer, 0, Send.Cursor)
Reliable:FireClient(Player, Buffer, Send.Instances)
Send.Size = 64
Send.Cursor = 0
Send.Buffer = buffer.create(64)
table.clear(Send.Instances)
end
end
RunService.Heartbeat:Connect(StepReplication)
Reliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: {Instance})
RecieveCursor = 0
RecieveBuffer = Buffer
RecieveInstances = Instances
RecieveInstanceCursor = 0
local Size = buffer.len(RecieveBuffer)
while (RecieveCursor < Size) do
-- Read BLOCK: 1 bytes
local BLOCK_START = Read(1)
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
end
end)
Unreliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: {Instance})
RecieveCursor = 0
RecieveBuffer = Buffer
RecieveInstances = Instances
RecieveInstanceCursor = 0
local Size = buffer.len(RecieveBuffer)
while (RecieveCursor < Size) do
-- Read BLOCK: 1 bytes
local BLOCK_START = Read(1)
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
end
end)
local Returns = table.freeze({
step_replication = StepReplication,
replicate_data = {
fire = function(Player: Player, Value: buffer): ()
Load(PlayersMap[Player])
Types.WriteEVENT_replicate_data(Value)
PlayersMap[Player] = Save()
end,
fire_all = function(Value: buffer): ()
Load()
Types.WriteEVENT_replicate_data(Value)
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
for _, Player in Players:GetPlayers() do
Load(PlayersMap[Player])
local Position = Allocate(Size)
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
PlayersMap[Player] = Save()
end
end,
fire_list = function(List: {Player}, Value: buffer): ()
Load()
Types.WriteEVENT_replicate_data(Value)
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
for _, Player in List do
Load(PlayersMap[Player])
local Position = Allocate(Size)
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
PlayersMap[Player] = Save()
end
end,
fire_except = function(Except: Player, Value: buffer): ()
Load()
Types.WriteEVENT_replicate_data(Value)
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
for _, Player in Players:GetPlayers() do
if Player == Except then
continue
end
Load(PlayersMap[Player])
local Position = Allocate(Size)
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
PlayersMap[Player] = Save()
end
end,
},
})
type BLINK_EVENTS_SYMBOL = typeof(Returns)
return Returns :: BLINK_EVENTS_SYMBOL

153
src/net/types.luau Normal file
View file

@ -0,0 +1,153 @@
--!strict
--!native
--!optimize 2
--!nolint LocalShadow
--#selene: allow(shadowing)
-- File generated by Blink v0.14.15 (https://github.com/1Axen/Blink)
-- This file is not meant to be edited
local Invocations = 0
local SendSize = 64
local SendOffset = 0
local SendCursor = 0
local SendBuffer = buffer.create(64)
local SendInstances = {}
local RecieveCursor = 0
local RecieveBuffer = buffer.create(64)
local RecieveInstances = {}
local RecieveInstanceCursor = 0
type Entry = {
value: any,
next: Entry?
}
type Queue = {
head: Entry?,
tail: Entry?
}
type BufferSave = {
Size: number,
Cursor: number,
Buffer: buffer,
Instances: {Instance}
}
local function Read(Bytes: number)
local Offset = RecieveCursor
RecieveCursor += Bytes
return Offset
end
local function Save(): BufferSave
return {
Size = SendSize,
Cursor = SendCursor,
Buffer = SendBuffer,
Instances = SendInstances
}
end
local function Load(Save: BufferSave?)
if Save then
SendSize = Save.Size
SendCursor = Save.Cursor
SendOffset = Save.Cursor
SendBuffer = Save.Buffer
SendInstances = Save.Instances
return
end
SendSize = 64
SendCursor = 0
SendOffset = 0
SendBuffer = buffer.create(64)
SendInstances = {}
end
local function Invoke()
if Invocations == 255 then
Invocations = 0
end
local Invocation = Invocations
Invocations += 1
return Invocation
end
local function Allocate(Bytes: number)
local InUse = (SendCursor + Bytes)
if InUse > SendSize then
--> Avoid resizing the buffer for every write
while InUse > SendSize do
SendSize *= 1.5
end
local Buffer = buffer.create(SendSize)
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
SendBuffer = Buffer
end
SendOffset = SendCursor
SendCursor += Bytes
return SendOffset
end
local function CreateQueue(): Queue
return {
head = nil,
tail = nil
}
end
local function Pop(queue: Queue): any
local head = queue.head
if head == nil then
return
end
queue.head = head.next
return head.value
end
local function Push(queue: Queue, value: any)
local entry: Entry = {
value = value,
next = nil
}
if queue.tail ~= nil then
queue.tail.next = entry
end
queue.tail = entry
if queue.head == nil then
queue.head = entry
end
end
local Types = {}
local Calls = table.create(256)
local Events: any = {
Reliable = table.create(256),
Unreliable = table.create(256)
}
local Queue: any = {
Reliable = table.create(256),
Unreliable = table.create(256)
}
return {
}

15
src/server/data.luau Normal file
View file

@ -0,0 +1,15 @@
--!strict
local sapphire_data = require("@pkg/sapphire_data")
export type data = {
coins: number,
}
local template: data = {
coins = 0,
}
return sapphire_data.server({
template = template,
store_name = "player_data",
})

View file

@ -0,0 +1,16 @@
--!strict
local sapphire = require("@pkg/sapphire")
local data = require("@server/data")
local ecs = require("@pkg/sapphire_jecs")
local lifecycles = require("@pkg/sapphire_lifecycles")
local services = script.Parent.services
sapphire()
:register_singletons(services)
:register_singletons(services.systems)
:use(data)
:use(ecs)
:use(lifecycles)
:start()

View file

@ -0,0 +1,22 @@
--!strict
local data = require("@server/data")
local net = require("@net/server")
local function init()
-- this fires every time the data is changed including initial data load
data.on_data_changed(function(player)
local difference = data.calculate_difference(player)
if not difference then
-- weird.. but ok
return
end
net.replicate_data.fire(player, difference)
end)
end
return {
init = init,
-- data is important
priority = math.huge,
}

View file

@ -0,0 +1,31 @@
--!strict
local Players = game:GetService("Players")
local ecs = require("@pkg/sapphire_jecs")
local ref = ecs.ref
local components = require("@shared/components")
local cplayer = components.player
local types = require("@shared/types")
local util = require("@shared/util")
type player = types.RobloxPlayer
type character = types.R15Character
local player_added = ecs.collect(Players.PlayerAdded)
local player_removing = ecs.collect(Players.PlayerRemoving)
local function system()
for _, player: player in player_added do
ref(player.UserId):set(cplayer, player)
end
for _, player: player in player_removing do
ref(player.UserId):delete()
end
end
return {
system = util.wrap_system(system),
phase = "heartbeat",
}

View file

@ -0,0 +1,33 @@
--!strict
local ecs = require("@pkg/sapphire_jecs")
local jecs = ecs.jecs
local world = ecs.world
type entity<T = nil> = ecs.entity<T>
local types = require("@shared/types")
-- change the `Character` type in `types` to whichever character your game is using
type character = types.Character
type player = types.RobloxPlayer<character>
return {
player = world:component() :: entity<player>,
world_model = world:component() :: entity<BasePart>,
-- jecs reexports
on_add = jecs.OnAdd,
on_remove = jecs.OnRemove,
on_set = jecs.OnSet,
child_of = jecs.ChildOf,
componente = jecs.Component,
wildcard = jecs.Wildcard,
w = jecs.Wildcard,
on_delete = jecs.OnDelete,
on_delete_target = jecs.OnDeleteTarget,
delete = jecs.Delete,
remove = jecs.Remove,
name = jecs.Name,
rest = jecs.Rest,
pair = jecs.pair,
}

182
src/shared/types.luau Normal file
View file

@ -0,0 +1,182 @@
--!strict
export type R15CharacterChildren = {
Humanoid: Humanoid & {
Animator: Animator,
},
Shirt: Shirt,
Pants: Pants,
["Body Colors"]: BodyColors,
["Shirt Graphic"]: ShirtGraphic,
HumanoidRootPart: Part & {
Climbing: Sound,
Died: Sound,
FreeFalling: Sound,
GettingUp: Sound,
Jumping: Sound,
Landing: Sound,
Running: Sound,
Splash: Sound,
Swimming: Sound,
RootRigAttachment: Attachment,
},
Head: MeshPart & {
FaceCenterAttachment: Attachment,
FaceFrontAttachment: Attachment,
HairAttachment: Attachment,
HatAttachment: Attachment,
NeckRigAttachment: Attachment,
Neck: Motor6D,
},
LeftFoot: MeshPart & {
LeftAnkleRigAttachment: Attachment,
LeftFootAttachment: Attachment,
LeftFoot: WrapTarget,
LeftAnkle: Motor6D,
},
RightFoot: MeshPart & {
RightAnkleRigAttachment: Attachment,
RightFootAttachment: Attachment,
RightFoot: WrapTarget,
RightAnkle: Motor6D,
},
LeftLowerLeg: MeshPart & {
LeftLowerLeg: WrapTarget,
},
RightLowerLeg: MeshPart & {
RightLowerLeg: WrapTarget,
},
LeftUpperLeg: MeshPart & {
LeftUpperLeg: WrapTarget,
},
RightUpperLeg: MeshPart & {
RightUpperLeg: WrapTarget,
},
LowerTorso: MeshPart & {
LowerTorso: WrapTarget,
},
UpperTorso: MeshPart & {
UpperTorso: WrapTarget,
},
RightHand: MeshPart & {
RightHand: WrapTarget,
},
LeftHand: MeshPart & {
LeftHand: WrapTarget,
},
RightLowerArm: MeshPart & {
RightLowerArm: WrapTarget,
},
RightUpperArm: MeshPart & {
RightUpperArm: WrapTarget,
},
LeftLowerArm: MeshPart & {
LeftElbowRigAttachment: Attachment,
LeftWristAttachment: Attachment,
LeftLowerArm: WrapTarget,
LeftElbow: Motor6D,
},
LeftUpperArm: MeshPart & {
LeftElbowRigAttachment: Attachment,
LeftShoulderAttachment: Attachment,
LeftShoulderRigAttachment: Attachment,
LeftUpperArm: WrapTarget,
LeftShoulder: Motor6D,
},
}
export type R6CharacterChildren = {
Humanoid: Humanoid & {
Animator: Animator,
},
Shirt: Shirt,
Pants: Pants,
["Body Colors"]: BodyColors,
["Shirt Graphic"]: ShirtGraphic,
HumanoidRootPart: Part & {
Climbing: Sound,
Died: Sound,
FreeFalling: Sound,
GettingUp: Sound,
Jumping: Sound,
Landing: Sound,
Running: Sound,
Splash: Sound,
Swimming: Sound,
RootAttachment: Attachment,
RootJoint: Motor6D,
},
Head: Part & {
FaceCenterAttachment: Attachment,
FaceFrontAttachment: Attachment,
HairAttachment: Attachment,
HatAttachment: Attachment,
Mesh: SpecialMesh,
face: Decal,
},
Torso: Part & {
BodyBackAttachment: Attachment,
BodyFrontAttachment: Attachment,
LeftCollarAttachment: Attachment,
NeckAttachment: Attachment,
RightCollarAttachment: Attachment,
WaistBackAttachment: Attachment,
WaistCenterAttachment: Attachment,
WaistFrontAttachment: Attachment,
roblox: Decal,
["Left Hip"]: Motor6D,
["Left Shoulder"]: Motor6D,
Neck: Motor6D,
["Right Hip"]: Motor6D,
["Right Shoulder"]: Motor6D,
},
["Left Arm"]: Part & {
LeftGripAttachment: Attachment,
LeftShoulderAttachment: Attachment,
},
["Left Leg"]: Part & {
LeftFootAttachment: Attachment,
},
["Right Arm"]: Part & {
RightGripAttachment: Attachment,
RightShoulderAttachment: Attachment,
},
["Right Leg"]: Part & {
RightFootAttachment: Attachment,
},
}
export type R15Character = Model & Instance & R15CharacterChildren
export type R6Character = Model & Instance & R6CharacterChildren
-- change it to whatever youre using
export type Character = R15Character
export type RobloxPlayer<Character = Character> = Player & {
PlayerGui: PlayerGui & StarterGui & {
BubbleChat: ScreenGui,
Chat: ScreenGui,
Freecam: ScreenGui,
},
PlayerScripts: PlayerScripts & {
BubbleChat: LocalScript,
ChatScript: LocalScript,
PlayerScriptsLoader: LocalScript,
RbxCharacterSounds: LocalScript,
PlayerModule: ModuleScript,
},
Character: Character?,
CharacterAdded: RBXScriptSignal<Character>,
}
export type Ok<T> = {
kind: "ok",
value: T,
}
export type Err<E> = {
kind: "err",
value: E,
}
export type Result<T, E> = Ok<T> | Err<E>
return {}

28
src/shared/util.luau Normal file
View file

@ -0,0 +1,28 @@
--!strict
--[=[
Useful for wrapping systems for schedulers which take a function returning a system.
(
```lua
-- for example
local function system(world: world)
return function(dt: number)
...
end
end
```
)
@param system
@return () -> (T...) -> ()
]=]
local function wrap_system<T...>(system: (T...) -> ()): () -> (T...) -> ()
return function()
return system
end
end
return {
wrap_system = wrap_system,
}