Godot Version
v4.2.2.stable.mono.official [15073afe3]
Question
Someone help me on the System.NullReferenceException error
I added conditions to check saves, and the input of the user(because this error is in a popup and it’s a runtime error), and all of that was to add a password that was exclusive for the user that could be stored on a pendrive, your HDD/SSD, and maybe this is related to the GameData and SaveSystem scripts.
Here’s the scripts:
// GameInit.cs(MainMenu Scene node)
using Godot;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using SystemFileAccess = System.IO.FileAccess;
using GodotFileAccess = Godot.FileAccess;
public partial class GameInit : Control
{
private PopupMenu _popupMenu;
public SaveSystem saveSystem = new SaveSystem();
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
var saveData = saveSystem.LoadGame();
var DataExists = saveData["exists"];
if ((bool)DataExists != true) {
GameData gameDataClass = new GameData();
saveData = gameDataClass.MakeSave();
}
this.SetMeta("SaveData", saveData);
var hashedPassword = saveData["hashedPassword"];
if ((string)hashedPassword == "") {
DriveInfo[] drives = DriveInfo.GetDrives();
_popupMenu = GetNode<PopupMenu>("PopupMenu");
var i = 0;
foreach (DriveInfo drive in drives)
{
// The drive is ready?
if (drive.IsReady)
{
// Only Removable and Fixed Drives
if (drive.DriveType == DriveType.Removable || drive.DriveType == DriveType.Fixed)
{
_popupMenu.AddItem(drive.RootDirectory.FullName, i);
}
i++;
}
}
Callable callable = new Callable(this, "ChosenDrive");
_popupMenu.Connect("id_pressed", callable);
_popupMenu.PopupCentered();
}
}
public void ChosenDrive(int id) {
var saveData = (Godot.Collections.Dictionary<string, Godot.Variant>)this.GetMeta("SaveData");
if (saveData == null) {
GD.PrintErr("A problem ocurred when trying to create save");
return;
}
string drive = _popupMenu.GetItemText(id);
if (string.IsNullOrEmpty(drive)) {
GD.PrintErr("Drive is null or empty.");
return;
}
string guid = Guid.NewGuid().ToString();
saveData["selectedDrive"] = drive;
var fullPath = System.IO.Path.Combine(drive, "password.guid");
using var driveFile = GodotFileAccess.Open(fullPath, GodotFileAccess.ModeFlags.Write);
saveData["hashedPassword"] = GenerateSHA256Hash(guid);
driveFile.StoreLine(guid);
saveSystem.SaveGame(saveData);
this.SetMeta("DevAuthenticated", true);
this.SetMeta("SaveData", saveData);
}
private string GenerateSHA256Hash(string input)
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] bytes = Encoding.UTF8.GetBytes(input);
byte[] hashBytes = sha256.ComputeHash(bytes);
// Converte o hash para uma string hexadecimal
StringBuilder hashStringBuilder = new StringBuilder();
foreach (byte b in hashBytes)
{
hashStringBuilder.Append(b.ToString("x2"));
}
return hashStringBuilder.ToString();
}
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}
// SaveSystem.cs
using Godot;
using System;
using System.IO;
using SystemFileAccess = System.IO.FileAccess;
using GodotFileAccess = Godot.FileAccess;
public partial class SaveSystem : Node
{
public string fullPath;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
string executablePath = OS.GetExecutablePath();
string directoryPath = System.IO.Path.GetDirectoryName(executablePath);
fullPath = System.IO.Path.Combine(directoryPath, "savegame.save");
}
public void SaveGame(Godot.Collections.Dictionary<string, Variant> gameData)
{
using var saveFile = GodotFileAccess.Open(fullPath, GodotFileAccess.ModeFlags.Write);
var jsonString = Json.Stringify(gameData);
saveFile.StoreLine(jsonString);
}
// Note: This can be called from anywhere inside the tree. This function is
// path independent.
public Godot.Collections.Dictionary<string, Variant> LoadGame()
{
if (!GodotFileAccess.FileExists(fullPath))
{
return new Godot.Collections.Dictionary<string, Variant>()
{
{ "exists", false }
}; // Error! We don't have a save to load.
}
// Load the file line and process that dictionary to get the data
// it represents.
using var saveFile = GodotFileAccess.Open(fullPath, GodotFileAccess.ModeFlags.Read);
var jsonString = saveFile.GetLine();
var json = new Json();
var parseResult = json.Parse(jsonString);
if (parseResult != Error.Ok)
{
GD.Print($"An error ocurred while parsing the data: {json.GetErrorMessage()}, for confirming check your savegame.save file");
return new Godot.Collections.Dictionary<string, Variant>()
{
{ "exists", false }
};
}
var saveData = new Godot.Collections.Dictionary<string, Variant>((Godot.Collections.Dictionary)json.Data);
return saveData;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}
// GameData.cs
using Godot;
using System;
public partial class GameData : Node
{
public Godot.Collections.Dictionary<string, Variant> MakeSave()
{
var mainMenuThemeVidDic = new Godot.Collections.Dictionary<string, Variant>()
{
{ "sonicEntry", "" },
{ "sonicFingerMoving", "" }
};
Vector2 emptyActSize = new Vector2(100, 200);
var emptyActTilemap = new Godot.Collections.Array();
var emptyAct = new Godot.Collections.Dictionary<string, Variant>()
{
{ "Name", "Empty" },
{ "ZoneName", "Act" },
{ "Act", 0 },
{ "tilemapSize", emptyActSize },
{ "tilemap", emptyActTilemap }
};
var levelsArray = new Godot.Collections.Array()
{
emptyAct
};
return new Godot.Collections.Dictionary<string, Variant>()
{
{ "mainMenuThemeVid", mainMenuThemeVidDic },
{ "levels", levelsArray },
{ "hashedPassword", "" },
{ "selectedDrive", "" },
{ "exists", true }
};
}
}
. There are only 4 scripts in my project but the 4th one still has nothing, things will be put later.