kams
kams copied to clipboard
Container object breaks notify_observers
There appears to be an issue with container objects in KAMS. This issue is present in a vanilla, unmodified version of KAMS, so I know it has nothing to do with my edits.
- Start KAMS and connect as admin
-
ACREATE Container TestContainer
-
ASAVE
andRESTART
- Log into KAMS as admin again
You will be placed in the same room, with a catch: The container is missing, and the room is glitched. Any event that is meant to be passed to a player in that room will be infinitely duplicated, printing message after message until you get stack level too deep
, cutting off the flood of messages. The errors from the server while this is happening are the following:
Exception `SystemStackError' at C:/Ruby23-x64/lib/ruby/2.3.0/set.rb:83 - stack level too deep
[01/09/17 09:03:38 Inventory]: Exception occurred while iterating the (duplicate list of) members of this Gary
[01/09/17 09:03:38 Inventory]: stack level too deep
[01/09/17 09:03:38 Inventory]: #<SystemStackError: stack level too deep>
[01/09/17 09:03:38 Inventory]: C:/Ruby23-x64/lib/ruby/2.3.0/set.rb:83:in `new'
C:/Ruby23-x64/lib/ruby/2.3.0/set.rb:83:in `initialize'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:57:in `new'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:57:in `out_event'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:72:in `block in out_event'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:46:in `block in each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:45:in `each_value'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:45:in `each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/inventory.rb:59:in `each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:71:in `out_event'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:72:in `block in out_event'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:46:in `block in each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:45:in `each_value'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:45:in `each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/inventory.rb:59:in `each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:71:in `out_event'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:72:in `block in out_event'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:46:in `block in each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:45:in `each_value'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:45:in `each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/inventory.rb:59:in `each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:71:in `out_event'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/objects/container.rb:72:in `block in out_event'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:46:in `block in each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:45:in `each_value'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/gary.rb:45:in `each'
Repeated over and over, until the following at the end:
:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/login.rb:37:in `block in receive_data'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/login.rb:29:in `each'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/lib/login.rb:29:in `receive_data'
C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/eventmachine-1.2.0.1-x64-mingw32/lib/eventmachine.rb:194:in `run_machine'
C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/eventmachine-1.2.0.1-x64-mingw32/lib/eventmachine.rb:194:in `run'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/server.rb:40:in `initialize'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/server.rb:77:in `new'
C:/Users/Joshua.Murphy/RubymineProjects/KAMS_Office/kams-master/server.rb:77:in `<top (required)>'
C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/ruby-debug-ide-0.6.1.beta2/lib/ruby-debug-ide.rb:88:in `debug_load'
C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/ruby-debug-ide-0.6.1.beta2/lib/ruby-debug-ide.rb:88:in `debug_program'
C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/ruby-debug-ide-0.6.1.beta2/bin/rdebug-ide:130:in `<top (required)>'
-e:1:in `load'
-e:1:in `<main>'
Using ALIST
shows no reference to any containers, as though it was never loaded in. I have tried placing rooms in rooms (as they inherit from containers), but the issue is not present.
It appears that the infinite loop is due to the room containing itself. For some reason, it looks like restarting the server with a container inside a room seems to be putting the room with the container inside itself. Perhaps it is mistaking the container for itself when it is populating the room's inventory?
It appears that creating new containers in a room is replacing the old container. Apparently there is some problem differentiating containers from other containers?
Found the problem. After carefully reviewing the code and using debugging tools to follow variables, I found that the issue was that the room's goid was being sent to the manager's create_object
method as the GOID argument - in other words, the create_object
method was being told to create the object with the room's goid. This was because objects/container.rb
has an extra argument that other objects don't have - capacity
. capacity
came before *args
. Basically, when manager was running klass.new(nil, room_goid)
on a Container
or a subclass of Container
, the first argument (nil) was actually for the capacity
parameter, not for the first parameter of GameObject
, the parameter for GOID.
The solution was to add a simple check to the create_object
method in components/manager.rb. It checks if the specified class inherits from or equals Container
, and if so, puts room_goid
as the third argument, not the second.
#Better fix in below comment#
def create_object(klass, room = nil, args = nil, vars = nil)
object = nil
if room.is_a? Container
room_goid = room.goid
else
room_goid = room
room = $manager.get_object room
end
if args
if args.is_a? Enumerable
object = klass.new(*args)
else
object = klass.new(args)
end
else
#BEGIN ADDED CODE
klass == Container #Only executes if klass inherits from Container.
object = klass.new(nil, nil, room_goid)
#Containers have an added first argument, capacity.
else
#END ADDED CODE
object = klass.new(nil, room_goid)
end
end
I would publish this change, but I don't know how to use Git and if I'd even be capable of doing so. Anyways, the issue is resolved for now. This is a short-term solution as I have not stress tested this change, so it could break something else, or not work in certain circumstances. I'll have to look further into this change to determine if it's solid enough.
Found a more solid solution. The last solution was wonky and tended to break some things. Here is a better one:
Changed the initialize method in objects/container.rb to:
def initialize(*args, capacity: nil)
instead of:
def initialize(capacity = nil, *args)
Meaning that capacity will only be set if it is explicitly mentioned (I.E. Container.new(thing, 'otherthing', capacity: 1
)