From a962d226e1bd7f20c9099c804dc28c2aaeb25029 Mon Sep 17 00:00:00 2001 From: metatablecat <132796135+metatablecat@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:22:47 +0100 Subject: [PATCH] Fix spawning issues This is a breaking change, use Service:SpawnObject instead of Object:Spawn --- src/lib/Dispatcher.luau | 36 ++++++++++++++++++++++++++---------- src/lib/Error.luau | 4 +++- src/lib/Object.luau | 19 ++++++++----------- src/lib/Service.luau | 19 ++++++++++++++++++- src/lib/Types.luau | 13 +++++++------ 5 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/lib/Dispatcher.luau b/src/lib/Dispatcher.luau index 189baae..2e22b8d 100644 --- a/src/lib/Dispatcher.luau +++ b/src/lib/Dispatcher.luau @@ -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", @@ -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 @@ -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) @@ -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 @@ -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 @@ -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) diff --git a/src/lib/Error.luau b/src/lib/Error.luau index 1c1ce23..96e7399 100644 --- a/src/lib/Error.luau +++ b/src/lib/Error.luau @@ -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 diff --git a/src/lib/Object.luau b/src/lib/Object.luau index 5f0d9c4..480a6c8 100644 --- a/src/lib/Object.luau +++ b/src/lib/Object.luau @@ -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 = { @@ -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() @@ -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 diff --git a/src/lib/Service.luau b/src/lib/Service.luau index 12bb305..2528b21 100644 --- a/src/lib/Service.luau +++ b/src/lib/Service.luau @@ -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?", @@ -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) @@ -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 diff --git a/src/lib/Types.luau b/src/lib/Types.luau index 742da84..e251cd6 100644 --- a/src/lib/Types.luau +++ b/src/lib/Types.luau @@ -15,12 +15,6 @@ export type Object = { GetState: (Object) -> ObjectState, Destroy: (Object) -> (), - Spawn: ( - Object, - xpcallHandler: ((string?) -> string)?, - asyncHandler: (boolean, string?) -> ()? - ) -> (boolean, string?), - Update: (Object, dt: number) -> number?, Destroying: (Object) -> (), Init: (Object) -> (), @@ -45,6 +39,13 @@ export type Service = { Class: (Service, name: string, createObject: (Object) -> ()) -> Class, CreateObjectFromClass: (Service, Class, initParams: B?) -> Object, + SpawnObject: ( + Service, + BlankObject, + xpcallHandler: ((string?) -> string)?, + asyncHandler: (boolean, string?) -> ()? + ) -> (boolean, string?), + StartService: (Service) -> (), Spawning: (Service, BlankObject) -> (), Updating: (Service, BlankObject, dt: number) -> number?,