kams icon indicating copy to clipboard operation
kams copied to clipboard

Container object breaks notify_observers

Open Portalboy opened this issue 8 years ago • 5 comments

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.

  1. Start KAMS and connect as admin
  2. ACREATE Container TestContainer
  3. ASAVE and RESTART
  4. 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.

Portalboy avatar Jan 09 '17 15:01 Portalboy

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?

Portalboy avatar Jan 09 '17 15:01 Portalboy

It appears that creating new containers in a room is replacing the old container. Apparently there is some problem differentiating containers from other containers?

Portalboy avatar Jan 09 '17 15:01 Portalboy

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

Portalboy avatar Jan 09 '17 17:01 Portalboy

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.

Portalboy avatar Jan 09 '17 17:01 Portalboy

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)

Portalboy avatar Jan 09 '17 20:01 Portalboy