Best strategies to technically maintain an already released game?

My game is reaching the final stages of development and I’m already worried about how I will maintain it, fix bugs, create new versions, etc.
I use a highly complex database through DataStore. Obviously, I will not make changes, corrections, or improvements directly to the production DataStore before exhaustively testing these changes in a SandBox (parallel copy for development). Only after that, I’ll commit these changes to the production DataStore.

  1. How to have a Sandbox experience/game where I’ll copy all my DS to there, change it and then and then return everything to the production DS? i.e How to make full copies of a DS from one experience to another?
  2. What’s the best method to analyze bugs to fix bugs? Instead of recreating the wheel with LogService.MessageOut, I believe there are better solutions ready and for free. I’ve read about Sentry, also about GameAnalytics, but I was wondering if there is anything better for a starter game?
  3. What is the best way/platform to receive bug reports voluntarily generated by players?
1 Like

I would say Discord, but not if its geared towards <13 users, maybe a Roblox group? Or find a way to make a ticket system, someone probably has made one that can be found in #resources:community-resources.

You can log bugs in a datastore, just change the name of the player ti the entry number and have a store that has the total number of entries. This means you can call issues in order they are posted without including players names so players can opt to anonmously upload bug data.

As for maintaining the game make sure you get players inputs for bug reports outside of script errors or even future update plans.

As for pertaining the save data on testinf make a reserved server and fire it with join data so the server knows that it is a test server and can then enable changes made.

1 Like

You can make a staging place in your main game so that it shares the same datastores.

There’s no way to share datastores directly between universes (games). You’ll have to use Open Cloud web API and write a script to read from one and copy to the other.

If you need to test datastores, try using MockDataStoreService - seamless local development & testing with datastores, it allows you to set up datastores with a JSON blob so you always have the state you expect on start of testing.

Either of those services will work fine. Look for a library in #resources I guess.

Roblox doesn’t have a built-in log aggregation feature at the moment. I gave feedback to some analytics team that they should add it at some point in the past.

Discord or via an in-game feature, if in-game you can collect it however makes sense to you.

1 Like

It looks like you’re asking about migrating data to a new schema. This is much more complicated than you explain it here because as your game grows it will be infeasible to move data to a new version using this method, since people are always playing your game, so your migration needs to work on-the-fly and cannot be done eagerly (all at once).

The easiest way is to implement lazy transfer instead of eager transfer. There’s several ways you can go:

  • You could define a version field in your data that lets you know what format it has. Then you can define transformers that increment the version of the data from its current version to the next. If the version of a player’s data is multiple versions out of date, just keep chaining transformers until it’s up-to-date. You’ll have to keep the transformers defined in your game’s code for an indeterminate amount of time (or until you’re sure no player has that old a version of data anymore)

  • You can make sure your changes are always backwards-compatible (e.g. only adding or removing members, not changing type or structure), and define defaults for values if they are not defined. That way someone with v1 data can play in the v2 and you just fill out defaults for values they don’t yet have, and strip away values that are removed in v2.

The first is purer but requires more code and requires you to maintain lots of transformers. The second is easier to implement but more error-prone because you need to make sure whatever code you are using to read the values takes the default if it is not yet defined.

For global data (non-player), this doesn’t work as well and you may need eager transfer. You could do this by setting up the data with the new schema in a different datastore/key and then having a feature switch in your code that determines which value (old or new store) is being used. That way you can set up the data first and then switch over the game to use the new data once it is ready.

3 Likes

I decided to venture out to create my own script to store error messages in a Datastore.
However, I realized that it is much more complicated than it looked.
LogService.MessageOut is very limited and at the same time, there are apparently no other options available for capturing console events.
Here are some limitations of MessageOut that hinder and even make it impossible to correctly evaluate errors that have been later stored in a Datastore:

  1. It does not differentiate whether the message came from the client or from the server;
  2. If the message came from the client, there is no way to know which player the message is from;
  3. It does not provide the timestamp of the message (although this can be obtained manually in the script);

Now I’m wondering if there is really any effective solution to correctly detect the errors generated by the game, both on the client and on the server?

:warning: Datastores is for persistent storage, not for log/analytic/metric-like data. You should use a proper logging solution for this!

If you put it in Datastores, you will need to store data across multiple keys so that you don’t run into concurrency issues (servers overwriting each other on same key), and when you do that, you’ll have lots of problems trying to read the data again due to datastore read limits. You cannot search through the data, you cannot aggregate it, and cleanup of old data is a huge pain.

You definitely want to use GameAnalytics or Sentry here. They let you search, aggregate, make graphs, templatize, show reports and alerts, etc. GameAnalytics is entirely free.

Also, if you abuse datastores in this way and end up accidentally storing a lot of data, you can expect a knock on the door by DevRel to change how you use datastores. Use systems for the purpose they are intended for.

Just reiterating: this will blow up in your face one way or another. If you dump to datastores, you have little to no control how much data your game is storing, especially if exploiters are messing around with their client or you introduce an issue that spams the logs.

MessageOut should only give you client messages (or maybe both client and server in Studio), not server messages.

Maybe look at: ScriptContext.Error

It should only give you errors from the current device. (server/client)

2 Likes

Thank you very much for your explanation. You saved me a lot of time and a lot of frustration.
However, my biggest concern, at least for now, is to be able to correctly track errors in my game, seeking to know where this error was generated (client or server), which player generated it, and at what time.
I wonder if both Sentry and GameAnalytics provide this information?

I think you would connect to ScriptContext.Error both on the server and on the client, and when on the client you invoke some remote with those logs so that the server can collect them (and knows which player they are from).

Note that client logs will always be iffy / non-trustable since they are generated on the client, which the user fully controls (so an exploiter could manipulate the logs). At high volume though, it’s still useful to collect client logs this way, most of the logs will be from legitimate players.

It would be useful if some of the methods you mentioned above have this per-client info already. You could file an Engine Features request for a built-in method that tells you log, stack trace, log level, and origin/time info. Seems like all the ones Roblox provide currently are limited in some shape or form.

1 Like