Draft: Store prototype API


  • Dev Team

    @tun9an0 It's still to be decided. I see two options:

    • One store bucket for both resources (and you will be able to load only 5000 units of ghodium in it)

    • Two separate stores energyStore and ghodiumStore each with its own capacity.


  • Dev Team

    @xenofix Yes, implementing Store prototype only for general-purpose stores is also an option which we consider. But it will not allow for massive convenience improvement in the API for transfer/withdraw/whatever code reuse cases.

    Almost every structure depends on energy, so it's good to be never undefined. energy===0 is an ugly exception in store, but it's not ugly in 'other structures' like extensions, towers, spawns, etc.

    It will be undefined only in the Store prototype, but not in StructureSpawn.energy for example.


  • Dev Team

    OK, another idea came to my mind (top post updated): we can make Store prototype have getters for every resource types which will always return 0 for any resource, and never return undefined at all, i.e. we fix the inconsistency in favor of energy rather than in favor of non-energy:

    storage.store[RESOURCE_ENERGY] === 0 // true
    storage.store[RESOURCE_HYDROGEN] === 0 // true
    

    Enumeration will work only through properties that are greater than 0.

    👍


  • @artch I do appreciate the infrastructure concern but I do think it's kind of leaking here, I wouldn't model this as a store as it's obviously different from say a storage or container. Most of my code dealing with energy is kind of ortoganal to dealing with non-energy as is, checking structure.energy versus structure.store.energy otherwise is just shifting thing about. Another concern is store.energy though, the API currently guarentees .energy to be set. I am sure like me there will be a lot of people who deal with it like this also. undefined < 10 returns false, this will send some quite a few people astray I am sure. I will need to dig through a lot of code to get this fixed everywhere..



  • @artch How many resources were factories going to add again? I sure hope for the Game Object's footprint on the heap it will come through some proxy, otherwise each Structure that can potentially hold something will have this massive hash with 0 values in it...



  • @artch said in Draft: Store prototype API:

    • Two separate stores energyStore and ghodiumStore each with its own capacity.

    With any other property than store it's not a simple unified API anymore. It has to be one store or no store, but we don't win anything if we wrap number fields in xxxStore-objects.

    For developing I must disagree with making the energy and energyCapacity deprecated. If you know the structure you are using, then these fields are the preferred way to access these values. The function of store is just to simplify some code for generic transfer/withdraw.

    I like the idea with store-properties returning 0. I think what @TuN9aN0 pointed out is an implementation detail here. I would be against Proxy. Store-objects for Extension or Tower could be using a Prototype which is just a view to the Structure without own memory, which has a property defined in the prototype to be const 0 for each resourceType and energy is a getter to the (hopefully not deprecated) energy field of structure.


  • Dev Team

    @tun9an0 Database and API changes are irrelevant. They introduce separate optimizations for engine code and for player code, but they are not related to each other, we might deploy them in separate updates if we want to, but it seems logical to deploy them together. There is no "leaking" in that regard.

    As to your compatibility concern, please see the post above.

    How many resources were factories going to add again? I sure hope for the Game Object's footprint on the heap it will come through some proxy, otherwise each Structure that can potentially hold something will have this massive hash with 0 values in it...

    40 new resource types. But there is no real data for absent resources in a Store. Store.prototype will just contain a getter for every resource type which returns 0 if the underlying DB data object doesn't have the corresponding resource property.


  • Dev Team

    @xenofix said in Draft: Store prototype API:

    For developing I must disagree with making the energy and energyCapacity deprecated. If you know the structure you are using, then these fields are the preferred way to access these values.

    I don't get it, why exactly is extension.energy so much more convenient than extension.store.energy that we have to keep this ugly duplication forever?



  • @artch Ok well if it's not for infrastructure concerns, then my feedback is I do not need this change, I think it's currently fine. In my view it actually introduces a leaky abstraction. I don't need unified access to things that are essentially different logically. I would not model it this way.


  • Dev Team

    @tun9an0 Very well then, thanks for your feedback! Let's see what other people will say, since this inconsistency and ambiguity in how resources are stored is being long complained by many players.



  • To me, carry is inconsistent. I would want to have it be named store instead. And a Store-object for general purpose stores is also very welcomed.

    But for every other non-general-purpose structure this is only a little nice-to-have feature to simplify withdraw/transfer. If dealing with structure-specific code, I would prefer to access energy and other fields directly. If it's general purpose refiller/robbery code, then I would go for store.

    Important to be is that such a change does not have a negative performance impact.


  • Dev Team

    @xenofix

    If dealing with structure-specific code, I would prefer to access energy and other fields directly.

    Still can't seem to understand what's the difference between addressing .energy and .store.energy?

    As to performance, CPU-wise it will not be affected, only heap-wise concern is valid here, and we can simply raise the heap limit on deploying this change.


  • Culture

    As both a mod developer and player I'm exited about this update, for modding, it was always annoying not being able to iterate over resources in a simple way. As a player, the entire store/carry/energyCapacity/carryCapacity mess was annoying, in some cases, I actually wrapped all those to make them match, very similar to how this is designed. It might seem a bit more clunky for stuff with only one resource, but simplifies more than enough other code to make it worth it.

    For those wanting to keep energy/energyCapacity, they can easily do a simple prototype extension in their code. Object.defineProperty(StructureExtension.prototype, 'energy', { get() { return this.store.energy } }) Object.defineProperty(StructureExtension.prototype, 'energyCapacity', { get() { return this.store.capacity } })



  • @ags131 yeah I've had to abstract around energy, store etc in various places (e.g. if (instanceof_energy_structure(struc)) { } else if (instanceof_store_structure(struc)) { } )

    Before I used typescript and enforced this at "compile-time", I remember fixing various bugs where I tried to treat an extension as if it had a store, or some other structure the other way around.

    I will be glad to have a unified interface, even though I'll have to at some point rework some of my code.

    It will make the game slightly less complex for new players.



  • From my point of view, I like and welcome this change, it will allow me to streamline my creep states so they can exit faster (Thus saving CPU.) I am concerned that heap use may increase, but that's Artch's problem... and in reality, any heap issue impacts everyone, mostly heavy users and users past the 300 CPU limit first, so I don't have a problem with that, despite that I'm breathing down the 300 CPU limit's neck. At the end of the day, it's pretty fair.

    Two potential issues. First, the obvious one: Nukers with their 5000 ghodium. Two stores? Ewww! Don't make them all the same except for this one weird bird. Applying a limit object with, for example: {G:5000,energy:250000} to it is the best solution, other structures would have null there to short circuit any checks.

    Then the 800 pound Gorilla: LABS. Labs are incompatible with a Store object unless you dynamically reset the limit object on the fly. Sure, the capacity remains 5000, but it can change from (for example.) {UO:3000,energy:2000} to {energy:2000} to {KH:3000,energy:2000}. It can have only one non-energy resource as it uses that to determine what it's doing.



  • I like the idea of a unified way of withdrawing / transferring resources, specially with getters that return 0 instead of undefined. I already have something like that in my code. I would have to keep it, though, since mine also supports pickup and drop. I'm not sure this change would benefit me specifically, but I see its value for general purpose code. The only things I don't like about this proposal are including nuker into this, which seems like a stretch and doesn't really add any value, and deprecating the old API. Forcing everybody to change their code, making it slightly more convoluted or it will eventually break, will just make people angry. I see no benefit in that. Keep both and everybody is happy.


  • YP

    I also like the new structure .. I already had added prototypes for some of the features, like a cached storeSum and carrySum and having it official is a plus. If the api is decided I'd like to add a polyfill so I can already start to use the new store object 😉



  • I also welcome the API change but without deprecation of energy. If energy must be deprecated, then please make sure they are configurable to deactivate any warnings.

    I think professional players already know how to overwrite prototypes and have aliases and getters for their needs already. Everyone can already model a unified store, and properties for energy and whatever. Keeping that in mind, the API change is mainly for newbies and those who cannot change prototypes using typescript or have found ugly ways around the inconsistent API which they like to simplify.

    As a newbie I did often access creep.energy directly instead of creep.carry.energy. The same for storages, it was just so annoying that I always have to access either carry.energy or store.energy or energy when at first all I care about is just simple energy. The tower has no store, the creep has no energy and no store but carry instead; just not consistent. But I can make it consistent with prototypes. So yes, this change would not really help me, but it will help other newbies. One store for all kind of objects is a great change for the API.

    About the database change: Please just choose the most efficient model.

    I think every high-level player at 300 cpu cap and those who are looking forward to be one agrees with me that performance is more important than convenience. The database is hidden from us, so I prefer an efficient database model over a simple engine code. I have no idea if your proposed database change will have a positive or negative impact on overall performance, but I just fear that it's a negative impact because it looks more complex than before. In my eyes, the API and the database change are two different changes.

    In the end these proposed convenience changes should meet the following requirements:

    1. one store for every relevant object to allow a unified way to access energy and resources for convenience.
    2. It shall not increase overall tick rate, better decrease it.
    3. It shall not significantly decrease the free memory we can use
    4. It shall not increase our cpu consumption, better decrease it.

    If that's all met, then I believe every single player in the community will be happy about this change.


  • Dev Team

    @xenofix

    I have no idea if your proposed database change will have a positive or negative impact on overall performance, but I just fear that it's a negative impact because it looks more complex than before.

    It will be positive. Look at this engine code:

    exports.calcResources = function(object) {
        return _.sum(C.RESOURCES_ALL, i => typeof object[i] == 'object' ? object[i].amount : (object[i] || 0));
    };
    

    It will benefit greatly from this database change:

    exports.calcResources = function(object) {
        return _.sum(object.store);
    };
    

  • Dev Team

    @eduter

    Keep both and everybody is happy.

    This is exactly how legacy monsters are born 👾


Locked