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?,