Skip to content

Instantly share code, notes, and snippets.

@philronan
Created December 13, 2019 00:18
Show Gist options
  • Save philronan/557c71dfc1038edead0d6062fc23c8b4 to your computer and use it in GitHub Desktop.
Save philronan/557c71dfc1038edead0d6062fc23c8b4 to your computer and use it in GitHub Desktop.
Unity scorekeeper with secret token mechanism to make it harder to falsify scores
/**
*
* NOTE: I created this script to make it harder to falsify scores in Unity
* WebGL games. It's designed to sit between the objects that modify scores
* and the object(s) that display them to the user. If an object needs to
* modify the score, then (a) add this object to the scoreSetters[] aray,
* and (b) add the method `public void SetSharedSecret(ulong secret)` to
* receive and store a 64-bit secret value (guaranteed to be non-zero). For
* example:
*
* public class MyGameObject : MonoBehaviour
* {
* private ulong sharedSecret;
* [SerializeField] ScoreKeeper scoreKeeper;
*
* // Secret can only be set once: if non-zero, then it has been set.
* private void Awake()
* {
* sharedSecret = 0;
* }
*
* private void HaveSomePoints(int howMany)
* {
* scoreKeeper.AddToScore(howMany, sharedSecret);
* }
*
* public void SetSharedSecret(ulong secretValue)
* {
* if (sharedSecret == 0)
* {
* sharedSecret = secretValue;
* }
* }
* }
*
* This approach may in fact be overkill. It looks like the Javascript
* interface to Unity's object model via unityInstance.SendMessage() only
* supports calls with a single parameter. Any public method that has two
* or more parameters will be inaccessible from the Javascript console in
* the client's browser.
*
**/
using UnityEngine;
public class ScoreKeeper : MonoBehaviour
{
private int score;
private int hiScore;
private ulong sharedSecret;
private bool scoringDisabled;
[SerializeField] Scoreboard scoreboard;
[SerializeField] MonoBehaviour[] scoreSetters;
// Start is called before the first frame update
void Start()
{
score = 0;
hiScore = 0;
GenerateNewSecret();
scoringDisabled = false;
}
// Create a new 64-bit shared secret value when the game starts
// Random.Range doesn't support uint, so we need to call 3 times.
// I'm going to assume that Unity's Random class seeds itself with
// sufficient entropy. (This isn't a perfectly secure solution in
// any case.)
void GenerateNewSecret()
{
do
{
sharedSecret = (ulong)Random.Range(0, 0x100000) << 44;
sharedSecret += (ulong)Random.Range(0, 0x100000) << 24;
sharedSecret += (ulong)Random.Range(0, 0x1000000);
} while (sharedSecret == 0);
// Pass this value to all the attached objects
for (int i=0; i<scoreSetters.Length; i++)
{
scoreSetters[i].SendMessage("SetSharedSecret", sharedSecret);
}
}
public void AddToScore(int points, ulong secret)
{
if (secret != sharedSecret)
{
InvalidateScoring();
return;
}
if (scoringDisabled)
{
return;
}
score += points;
scoreboard.UpdateScoreDisplay(score);
if (score > hiScore)
{
hiScore = score;
scoreboard.UpdateHighScoreDisplay(hiScore);
}
}
public void ResetScore()
{
score = 0;
scoreboard.UpdateScoreDisplay(score);
}
private void InvalidateScoring()
{
ResetScore();
scoringDisabled = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment