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

Commit

Permalink
Fix spawning issues
Browse files Browse the repository at this point in the history
This is a breaking change, use Service:SpawnObject instead of Object:Spawn
  • Loading branch information
metatablecat committed Aug 4, 2024
1 parent 5bfe973 commit a962d22
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 29 deletions.
36 changes: 26 additions & 10 deletions src/lib/Dispatcher.luau
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ local Types = require("./Types")

local Dispatcher = {}
local objectDispatchState = {}
local OBJECT_DESTROYED_RETURN_MSG = "The object was destroyed"

local serviceStartState: {[Types.Service]: {
State: "suspended"|"running"|"finished",
Expand Down Expand Up @@ -37,7 +38,7 @@ local function getObjectStateError(o)
local state = Dispatcher.getObjectState(o)

if not state then
ERROR.DISPATCHER_DESTROYED_OBJECT(o:GetID(true))
ERROR.OBJECT_DESTROYED(o)
end

return state
Expand Down Expand Up @@ -137,14 +138,15 @@ end
function Dispatcher.stop(o, state)
-- already destroyed checks
if not state then return end
if not Dispatcher.isSelfAsyncCall(o) then end

-- not destroyed, kill running thread then free as false
if state.TimeoutThread then killThread(state.TimeoutThread) end
local isSelfCall = killThread(state.Thread)
free(state, false, OBJECT_DESTROYED_RETURN_MSG)

free(state, false, "Object has been destroyed before it could return")
if isSelfCall then coroutine.yield() end
if state.Thread then
local isSelfCall = killThread(state.Thread)
if isSelfCall then coroutine.yield() end
end
end

local function spawnObject(object, service, state, asyncMode)
Expand All @@ -164,14 +166,28 @@ local function spawnObject(object, service, state, asyncMode)
return nil
end

function Dispatcher.spawnObject(o, fPrivate, xpcallHandler, asyncHandler)
local state = getObjectStateError(o)
function Dispatcher.spawnObject(o, service, fPrivate, xpcallHandler, asyncHandler)
local state = Dispatcher.getObjectState(o)

if not state then
-- object is destroyed
if not asyncHandler then return false, OBJECT_DESTROYED_RETURN_MSG end
task.spawn(asyncHandler, false, OBJECT_DESTROYED_RETURN_MSG)
return
end

-- we would handle this from the service but its safer to handle here
-- if an object is dead we just safely cancel it
if service ~= fPrivate.Service then
ERROR.SERVICE_INVALID_CLASS()
end

state.TimeoutDisabled = fPrivate.TimeoutDisabled
state.AwaitFor = fPrivate.AwaitFor

-- basically new logic for Spawn
if state.Spawned then
ERROR:DISPATCHER_ALREADY_SPAWNED(o)
ERROR.DISPATCHER_ALREADY_SPAWNED(o)
end

if xpcallHandler then
Expand All @@ -191,7 +207,7 @@ end

function Dispatcher.slotAwait(o)
local state = Dispatcher.getObjectState(o)
if not state then return false, "The object was destroyed" end
if not state then return false, OBJECT_DESTROYED_RETURN_MSG end

if state.ErrMsg then
return false, state.ErrMsg
Expand All @@ -211,7 +227,7 @@ end

function Dispatcher.slotHandleAsync(o, asyncHandler)
local state = Dispatcher.getObjectState(o)
if not state then task.spawn(asyncHandler, false, "The object was destroyed") end
if not state then task.spawn(asyncHandler, false, OBJECT_DESTROYED_RETURN_MSG) end

if state.ErrMsg then
asyncHandler(false, state.ErrMsg)
Expand Down
4 changes: 3 additions & 1 deletion src/lib/Error.luau
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,17 @@ local ErrorBuffer = {
ANALYSIS_MODE_NOT_AVAILABLE = e("ANALYSIS_MODE_NOT_AVAILABLE", "Analysis mode cannot be used in %s", "E"),

DISPATCHER_ALREADY_SPAWNED = e("DISPATCHER_ALREADY_SPAWNED", "Object %s has already been spawned.", "E"),
DISPATCHER_DESTROYED_OBJECT = e("DISPATCHER_DESTROYED_OBJECT", "Object %s cannot be spawned because it has been destroyed.", "E"),
DISPATCHER_SPAWN_ERR = e("DISPATCHER_SPAWN_ERR", "An object experienced an error while spawning: %s", "W"),
DISPATCHER_TIMEOUT = e("DISPATCHER_TIMEOUT", "Object %s is taking a long time to intialise. If this is intentional, disable with `[meta \"TimeoutDisabled\"] = true`", "W"),

ACTION_OBJECT_NOT_READY = e("ACTION_OBJECT_NOT_READY", "Action %s cannot run yet because its object is not ready.", "E"),
ACTION_OBJECT_DESTROYED = e("ACTION_OBJECT_DESTROYED", "Action %s cannot be ran on a destroyed object.", "E"),

OBJECT_DESTROYED = e("OBJECT_DESTROYED", "Method cannot be used because Object %* was destroyed..", "E"),
OBJECT_SELF_AWAIT = e("OBJECT_SELF_AWAIT", "Object %s is awaiting upon itself and will never resolve. Use HandleAsync instead.", "W"),

SERVICE_NO_CLASSES = e("SERVICE_NO_CLASSES", "Service %s does not implement classes.", "E"),
SERVICE_INVALID_OBJECT = e("SERVICE_INVALID_OBJECT", "Service %s cannot spawn object %s", "E"),
SERVICE_UPDATING_DISABLED = e("SERVICE_UPDATING_DISABLED", "Updating is not enabled on service %s, yet it implements Updating. This can be fixed by adding `[meta \"EnableUpdating\"] = true` to your service definition.", "W"),

-- Remove in 0.5.1
Expand Down
19 changes: 8 additions & 11 deletions src/lib/Object.luau
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ local function OBJECT_REFLECTION_TEST(object, oName)
return object and object[Common.ObjectHeader], "BAD_SELF_CALL", oName
end

local OBJECT_PRIVATE = {}

return function(params: {[string]: any}, service)
return function(params: {[string]: any}, service, OBJECT_PRIVATE)
local raw, metakeys = Common.validateTable(params, "Object", OBJECT_PARAMS)

local private = {
Expand All @@ -36,13 +34,7 @@ return function(params: {[string]: any}, service)
raw.Name = params.Name or `CatworkAsyncObject`

function raw:Spawn(xpcallHandler, asyncHandler)
if Common.AnalysisMode then return end

REFLECTION.CUSTOM(1, "Object.Spawn", self, OBJECT_REFLECTION_TEST)
REFLECTION.ARG(2, "Object.Spawn", REFLECTION.OPT_FUNCTION, xpcallHandler)
REFLECTION.ARG(3, "Object.Spawn", REFLECTION.OPT_FUNCTION, asyncHandler)

return Dispatcher.spawnObject(self, OBJECT_PRIVATE[self], xpcallHandler, asyncHandler)
ERROR.DEPRECATED("Object.Spawn", "Service.SpawnObject")
end

function raw:Await()
Expand All @@ -60,8 +52,13 @@ return function(params: {[string]: any}, service)

function raw:GetID(full: boolean?)
REFLECTION.CUSTOM(1, "Object.GetID", self, OBJECT_REFLECTION_TEST)
REFLECTION.ARG(2, "Object.GetID", REFLECTION.OPT_BOOLEAN, full)
REFLECTION.ARG(2, "Object.GetID", REFLECTION.OPT_BOOLEAN, full)

local private = OBJECT_PRIVATE[self]
if not private then
ERROR.OBJECT_DESTROYED(self)
end

return full and OBJECT_PRIVATE[self].FullID or OBJECT_PRIVATE[self].ID
end

Expand Down
19 changes: 18 additions & 1 deletion src/lib/Service.luau
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ local Metakeys = require("../meta")

local ENABLE_CLASSES_METAKEY = Metakeys.export "EnableClasses"

local OBJECT_PRIVATE = {}
local SERVICE_PARAMS = {
Name = "string",
StartService = "function?",
Expand All @@ -31,8 +32,13 @@ local function CLASS_REFLECTION_ASSERT(class, fName, idx)
return class and class[Common.ClassHeader], "BAD_OBJECT", idx, fName, oType, "Class"
end

local function OBJECT_REFLECTION_TEST(object, fName, idx)
local oType = typeof(object)
return object and object[Common.ObjectHeader], "BAD_OBJECT", idx, fName, oType, "Object"
end

local function createObjectForService(params, service)
local o = Object(params, service)
local o = Object(params, service, OBJECT_PRIVATE)

if not Common.AnalysisMode then
Dispatcher.initObjectState(o)
Expand Down Expand Up @@ -86,6 +92,17 @@ return function(params)
return createObjectForService(params, self)
end

function raw:SpawnObject(obj, xpcallHandler, asyncHandler)
if Common.AnalysisMode then return end

REFLECTION.CUSTOM(1, "Service.SpawnObject", self, SERVICE_REFLECTION_TEST)
REFLECTION.CUSTOM(2, "Service.SpawnObject", obj, OBJECT_REFLECTION_TEST)
REFLECTION.ARG(3, "Service.SpawnObject", REFLECTION.OPT_FUNCTION, xpcallHandler)
REFLECTION.ARG(4, "Service.SpawnObject", REFLECTION.OPT_FUNCTION, asyncHandler)

return Dispatcher.spawnObject(obj, self, OBJECT_PRIVATE[obj], xpcallHandler, asyncHandler)
end

if not raw.Spawning then
function raw:Spawning(object)
local i = object.Init
Expand Down
13 changes: 7 additions & 6 deletions src/lib/Types.luau
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@ export type Object<A> = {
GetState: (Object<A>) -> ObjectState,

Destroy: (Object<A>) -> (),
Spawn: (
Object<A>,
xpcallHandler: ((string?) -> string)?,
asyncHandler: (boolean, string?) -> ()?
) -> (boolean, string?),

Update: (Object<A>, dt: number) -> number?,
Destroying: (Object<A>) -> (),
Init: (Object<A>) -> (),
Expand All @@ -45,6 +39,13 @@ export type Service = {
Class: <A>(Service, name: string, createObject: (Object<A>) -> ()) -> Class<A>,
CreateObjectFromClass: <A, B>(Service, Class<A>, initParams: B?) -> Object<A & B>,

SpawnObject: (
Service,
BlankObject,
xpcallHandler: ((string?) -> string)?,
asyncHandler: (boolean, string?) -> ()?
) -> (boolean, string?),

StartService: (Service) -> (),
Spawning: (Service, BlankObject) -> (),
Updating: (Service, BlankObject, dt: number) -> number?,
Expand Down

0 comments on commit a962d22

Please sign in to comment.