Environments
For those who stumble across this card and are wondering what an 'Environment' is: imagine a game where players have their own personal section of land. Within this local area, they can configure their own admin roles, settings, bans, etc which saves. Everything which takes place within this area though remains within this area. An area can be anything from a plot of land in a server, to a private server. This in essence is an 'environment'. The goal with Nanoblox is to allow the easy creation of these environments, while ensuring restrictions are in place to prevent abuse. By default, there is the 'Game/Global' environment, where changes impact every server (i.e. the default behaviour you'd expect of admin commands).
Ben's latest thoughts and proposals:
1. APIs should be simplified
- For example,
RoleService.createRole(isGlobal, properties)should just becomeRoleService.createRole(properties), where 'isGlobal' and 'environment' now become properties. Consider renaming 'isGlobal' to 'permanent'. These properties default tofalseandGame/Global.
2. Redesign how environments are defined
- Instead of enums, consider strings, e.g.
"Game"instead ofmain.enum.environment.Game - Considerations:
- [ ] How and when are these environments created? For example, the Game is one entity, while a PrivateServer can have many different private server owners. Same for zone environments.
- [ ] This should be responsible for creating the separate datastore entities too - every environment will own its own SystemStore key (instead of roles, bans, etc)
- [ ] Also consider how a record may change datastore entities when its environment is changed - for this, remove the record then re-add it to the new environment
- [ ] Restrict the changing of environments if it does not exist
- [ ] An environment module with a function like .getEnvironment which returns a promise. If that environment has not been loaded before, then initiate it and continue afterwards.
- [ ] Have a module where environments are created, then create the default environments:
- [ ] Game
- [ ] PrivateServer
- [ ] ReservedServer - maybe combine this with reserved
- [ ] Remove
SettingService.createPrivateEnvironmentIfA
3. Every record needs to have a UID
- For example, instead of
bans["82347291"]it would bebans["82347291-IF9jL5UoJZ"]with a record property 'userId' that points to that user - This includes Settings, where instead of having
settings["Player"]andsettings["System"]it should besettings["Player-AbUkL5UoJZ"]with a record that contains the keys 'player' and 'system'
4. Every System Service needs to change
-
Every SystemService should have an associated object. RoleService would have 'Role', BanService would have 'Ban', etc. These objects will then contain the default values (instead of .generateRecord), the key 'record' (a State table where all display level values are stored), a default method
:update(), which is the equivalent of doing.record:set(), and a a signalchangedwhich mirrors .record.changed - When :destroy() is called on one of these objects, it truly erases the data
-
RoleService.recordAdded:Connect(function(roleUID, record)should becomeRoleService.roleAdded:Connect(function(role)and the same forroleRemoved, whileroleChangedshould be completely eliminated as the role object itself now has its own signal. Repeat for all other services -
SystemService.getObject(UID)should always be the same, while services can have alternative getters such asRoleService.getRoleByName(name, environment),BanService.getBanByUserId(userId, environment), etc
5. Virtually everything should use 'pathways'
- As the title says
- Consider having 'changed' to achieve this (instead of descendantChangedSignal)
6. How system items are displayed
- Items (such as roles, bans, etc) should be organised into environments. If no environments are present, then simply dont display the environment title at all. Something relatively similar to HD Admin's servers category:
- https://user-images.githubusercontent.com/51117782/127743960-dfa4e426-7d62-4236-85e1-1f63e0af6951.png
7. Belonging to multiple environments
- Within Command verification, if a user performing an action where it's uncertain which environment to act upon (e.g. if a game owner goes into someone private server, and the private server owner gives them admin), a prompt should appear informing the user to select their preferred environment and then taking them to the Player Settings page to select this.
- More details below
8. Objects/services now have to consider environments
- Objects/services like bans will now have to account for environments when calling methods such as getHighestRole
9. Reconsider some of the challenges posed by the current System and ConfigService
- This includes:
- [ ] The retrieval of new config information every 10 seconds
- [ ] The removal of items (such as roles) in game originally created in studio
- [ ] The global updating of these roles
10. GDPR Compliance
- [ ] When a user is wiped, make sure to wipe all their associated environments (reserved, private, etc) too
Currently Nanoblox has a 'global' environment where data can be saved (such as bans, roles, settings, etc).
Now consider how a similar environment can be setup for private servers and servers created through the SavePlaceAPI: https://developer.roblox.com/en-us/api-reference/class/AssetService
Also consider how zone-based environments can be created. For example, a game where everyone has an assigned piece of land, and only admin commands can be used in that zone on players in that zone. The environment can be associated to a player, but can also persist after this player leaves.
This will enable private server owners for example to permanently ban people and create saveable roles within their own private place whilst no spilling out into the rest of the game universe.
Revamp System user.temp.descendantChanged
- [ ] What happens if someone is banned on both Private and Global within surface level??????????????????????
- [ ] Important Consider how getters will work? Do you have to specify the environment? If so, a lot of methods and places will have to be updated (e.g. Args getSystemSetting/getPlayerSetting). Also consider what happens to state tables like that from CommandService (e.g. 'lowerCaseNameAndAliasToCommandDictionary') and SettingService (e.g. 'lowerCaseColorNames')
- [ ] Strongly consider reorganising surface level data (i.e. records) and events so that they have to be updated/set via a 'settingPathway' (e.g.
"RoleSJDSFKLSDF.limitModifiers.global"), and then callServiceName.objectChangedwith the pathwayTable instead of settingName. Utilise createDesendantChanged (maybe) for this.- [ ] Rename 'pathwaySetting' to 'pathwayString' and then also have 'pathwayTable'. Make sure to do the same for the RoleService methods (which call it 'pathwaySetting')
- [ ] Update the following services.recordChanged and give their objects 'get' and 'getOrSetup' methods which are mimiced from State
- [ ] TaskService
- [ ] RoleService
- [ ] Where possible, try to merge TOP LAYER and OTHER LAYERS into one, neaten up
- [ ] Completely revamp 'expiryTime' so instead of working for just the top layer, it can work for any layer (i.e. instead of doing
record[key] = nil, dorecord:set(nil) - [ ] Finally finish off the color changer detector table within
SettingService.loaded()
Other
- [x] Play around and compare SavePlaceAPI with Reserved Servers
- [x] Consider changing 'PrivateServer' Environment enum to just 'Private'
- [x] Reserved and Private servers have a unique
game.PrivateServerIdvalue, while CreatePlaceAsync Servers have a uniquegame.PlaceIdvalue. Consider a way then to initialise a 'private' environment with just a normal PlaceId. - [x] Consider an 'environment' enum for Roles. Items can include 'Global' and 'Private' (which includes Reserved Servers). This environment property then determines how all other actions behave too. For instance, if a player ('Private' environment as their dominant role) 'perm bans' someone, that person will only be banned from their private server, not the whole game (i.e. global).
- [ ] Reconfigure System:
- [ ] Consider revamping system user data so that it contains bans, roles, etc within a single user, instead of spread out over many, then whenever a private server is created, create a new system user
- [ ] Create a
SystemServicewith single method.createEnvironment(key) - [ ] Create an Environment object which contains the data loading/saving code
- [ ] Move most of system handler code to Environment, while only keeping the 'Display' data which fires the surface level events and contains the surface level API
- [ ] on :start(), have SystemService automatically create a 'Global' environment
- [ ] check SettingService.createPrivateEnvironmentIfA, then create an additional environment with the
game.PrivateServerIdif...- [ ]
privateServeristrueandgame.PrivateServerIdandgame.PrivateServerOwnerId > 0 - [ ] OR
reservedServeristrueandgame.PrivateServerIdandgame.PrivateServerOwnerId == 0 - [ ] OR
normalServeristrueandgame.PrivateServerId == nil
- [ ]
- [ ] ensure this new setup works correctly with
ConfigService - [ ] have ConfigService.setupComplete fire and set when Global data is finished, but don't worry about any additional environments
- [ ] this means if a non-global environment is called (e.g. :createRecord()), that will fist have to check the environment exists, then yield if not loaded. ensure all APIs handle promises.
- [ ] Introduce a third enum 'Multiple'. For all _isGlobal params within System objects (to be replaced with Environment), if 'Mulitple' is passed through, request the caller to specify. If caller player not present or no return within 10 seconds, default to 'PrivateServer'
- [ ] Introduce an environment enum arg/param for System (e.g. :createRecord, etc), replace _global with isPermanent, and then update to all system services
- [ ] Determine how roles can be hidden (for example, on a PrivateServer, you wont want to see Globals unless you're the game owner). Maybe split them into two categories for the UI (bans, roles, etc): 'Global' and 'Private Server'
- [ ] Ensure Global Receivers and Senders also account for environment (for example, even if Global, if Private environment, only send to servers with the matching id)
To do for now
Brainstorm
- [x] The methods, property and design of:
- [x] How environments and systems interconnect
- [x] EnvironmentService
- [x] getEnvironment -- if does not exist, warn
- [ ] getEnvironmentWhenLoaded -- returns promise which resolves when environment loaded all services
- [x] bindEnvironmentType(environmentType, action)
- [x] bind default ones:
- [x] global
- [x] privateServer
- [x] reservedServer
- [x] bind default ones:
- [x] unbindEnvironmentType(environmentType)
- [ ] canBroadcastGlobally - default false, if remains false ignore changes
- [x] Environment (object)
Mostly System / Environments
- [x] Create ban and unban command
- [x] See RoleService 'Role Order' and consolidate that: https://github.com/nanoblox/core/issues/102
- [x] Create objects
- [x] Have parent object called 'Record'
- [x] A property settings which is the record
- [x] Think how expirys will work, be hidden and eventually removed when the original player joins the server
- [x] Simply have a
record:hasExpired(pathwayToTable)method and then check for this within BanService and LongtermService - [x] Have
record.changedpoint torecord.settings.changed - [x]
record:update(settingsPathway, value)which handles the abstraction recordChanged does - [x] ensure createRecord and removeRecord work properly (e.g. account for environment, return object, destroy object, nil from dictionary)
- [ ] A method within services to associate properties (such as names, userids, environments, etc) to the object
- [ ] Make sure to remain environment specific!!
- [ ] System:getRecordsWithSettings(...)
- [ ] System:getRecordsWithSetting(pathway, environment) - if first time, sets up a records descendant changed signal and association table
- [ ] getRecordWithSetting - same as above but results[1]
- [ ] Make sure it functions like current TaskService setup
- [ ] Convert TaskService to this new method
- [x] All service method APIs should have an end
environmentparamter. Ifnil, this will default to the global environment object- [x] Change environment key to environmentUID
- [x] Setup an environment service and module
- [x] Environment.getUIDFromType()
- [x] bind and unbind methods which determines how UIDFromType works
- [ ] CONSIDER HOW NON-GLOBAL ENVIRONMENTTYPE ROLES WORK (e.g. PrivateService, ReservedServer, etc)
- [ ] We still want them to remain on the Global environment, but to create different environments when criteria is met
- [ ] It's also important that when a role has the global UID but a non-global Type then it gets disabled for that server. Consider an 'active' property for roles that when set to false then wipes it from the user role information records
- [ ] ensure change differences within studio/config then apply ingame for all environments
- [ ] completely setup config (see more details below)
- [ ] use getUIDFromType within config and have the environmentUID set before being added into the temp/perm records
- [ ] Introduce a method/signal where you can listen for environments loaded, then in framework, wait for Global environment to load before completing initialisation
- [ ] Introduce a
job.environmentwhich is passed down from CommandService verification
- [ ] Setup Record System objects:
- [ ] Ban
- [ ] Command
- [ ] Job
- [ ] Longterm
- [ ] Role
- [ ] Setting
- [ ] Warn
- [ ] For the following services...
- [ ] Update their events/signals
- [ ] Update all methods to include environments (and to remove isPerm/Global)
- [ ] Sevices:
- [ ] BanService
- [ ] CommandService
- [ ] JobService
- [ ] LongtermService
- [ ] RoleService
- [ ] SettingService
- [ ] WarnService
Additional
- [ ] Ensure methods like Args getSystemSetting/getPlayerSetting) are fully updated with environments
- [ ] Update state tables such as the following so that they work for multiple environment, instead of single initialisation
- [ ] CommandService 'lowerCaseNameAndAliasToCommandDictionary') -- IGNORE THIS, ITS ACTUALLY FINE
- [ ] SettingService (e.g. 'lowerCaseColorNames')
- [ ] Revamp all record:getTable and setTable
- [ ] TaskService everything
- [ ] THINK ABOUT THIS: Everything related to Framework.loaded needs to be revamped
- [ ] This method needs to entirely disappear
- [ ] Maybe an environmentLoaded alternative which can be listened for?
- [ ] What happens about framework:waitUntilLoaded? Should this wait be removed entirely, but then how do you ensure an environment is loaded when you wish to retrieve information about it? Maybe a wait until specific environment loaded event/method instead?
- [ ] For example, what happens about
BanService.playerLoadedMethodif this is called before the environment loaded?
- [ ] Config
- [ ] Update all config (bans, roles, etc) with EnvironmentType
- [ ] When config is loaded in game, sort based upon its EnvironmentType
- [ ] Setup an 'Individual' EnvironmentType Enum (and replace with Other)
Test
- [ ] Expiry on different layers of records work (and state:add())
- [ ] Changing a records environmentType and environmentUID
- [ ] The creating and saving of records, such as bans, in non global environments like private servers
- [ ] The changing of default information within config/studio after already creating and changing things within a non global env
Networking
- [ ] User information which determines if they have the main menu open/closed
- [ ] User information to determine which pages/info they can access
- [ ] A remote event fired to server to initially retrieve information and inform server menu is open/closed
- [ ] The remote event listened on client to handle system/record data changes
- [ ] Make sure to filter out tables which have an expired expiryTime
- [ ] Have system/record deduce who needs updating and can access information, and fire off accordingly
Data Caps
- [ ] Explore DataStore limits. Do new datastores need to be created per system instead of keys?
- [ ] Consider creating an arg called 'recordString'. It behaves the same as a normal string but gets capped to around 200 characters
- [ ] A Cap on the total number of record types (bans, roles, etc)