feat: base

Signed-off-by: Avior <github@avior.me>
This commit is contained in:
2025-07-15 00:46:55 +02:00
commit bcfc8fbc81
20 changed files with 498 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
extends Node
signal money_changed(new_money)
var _save_dir := "user://saves"
var data := {
"money": 1000,
"day": 1
}
func list_saves():
var dir := DirAccess.open("user://saves")
var saves = dir.get_files()
var res: Array[Save] = []
for save in saves:
res.append(Save.new())
func add_money(amount):
data.money += amount
emit_signal("money_changed", data.money)
var save_path = "user://save.bin"
func save_save():
var file = FileAccess.open(save_path, FileAccess.WRITE)
file.store_buffer(JSONPacker.encode(data))
func load_save(path: String):
if FileAccess.file_exists(path):
var file = FileAccess.open(path, FileAccess.READ)
data = JSONPacker.decode(file.get_buffer(file.get_length()))
return true
return false
func _init() -> void:
load_state()
func _notification(what):
if what == NOTIFICATION_WM_CLOSE_REQUEST:
save_state()
get_tree().quit() # default behavior

View File

@@ -0,0 +1 @@
uid://nkr0vilsuhfk

View File

@@ -0,0 +1,5 @@
extends Resource
class_name Save
@export var money: int
@export var day: int

View File

@@ -0,0 +1 @@
uid://b7g047l14kcxh

View File

@@ -0,0 +1,264 @@
class_name JSONPacker
enum Type {
NUM = 0x00,
LIT = 0x40,
ARR = 0x80,
BOL = 0xA0,
OBJ = 0xC0,
STR = 0xE0,
MULTI = 0x10,
INT = 0x00,
FLOAT = 0x04,
L8 = 0x00,
L16 = 0x01,
L32 = 0x02,
L64 = 0x03,
NULL = 0x00,
UNDEFINED = 0x01,
NAN = 0x02
}
static func encode(value) -> PackedByteArray:
if typeof(value) == TYPE_NIL:
#print("packing NIL")
return PackedByteArray([Type.LIT | Type.NULL])
elif typeof(value) == TYPE_BOOL:
#print("packing bool")
return PackedByteArray([Type.BOL | (1 if value else 0)])
elif typeof(value) == TYPE_INT or typeof(value) == TYPE_FLOAT:
#print("packing int")
if typeof(value) == TYPE_FLOAT and is_nan(value):
#print("packing float")
return PackedByteArray([Type.LIT | Type.NAN])
var encoded = _encode_number(value)
if encoded.type == 0:
return encoded.data
return _concat([PackedByteArray([Type.NUM | encoded.type]), encoded.data])
elif typeof(value) == TYPE_STRING:
#print("packing string")
var utf8 = value.to_utf8_buffer()
var length = utf8.size()
if length > 0x0F:
var encoded = _encode_number(length)
return _concat([PackedByteArray([Type.STR | encoded.type]), encoded.data, utf8])
return _concat([PackedByteArray([Type.STR | length]), utf8])
elif typeof(value) == TYPE_ARRAY:
#print("packing array")
var length = value.size()
var items = []
if length > 0x0F:
var encoded = _encode_number(length)
items.append(PackedByteArray([Type.ARR | encoded.type]))
items.append(encoded.data)
else:
items.append(PackedByteArray([Type.ARR | length]))
for v in value:
items.append(encode(v))
return _concat(items)
elif typeof(value) == TYPE_DICTIONARY:
#print("packing object")
var keys = value.keys()
var items = []
if keys.size() > 0x0F:
var encoded = _encode_number(keys.size())
items.append(PackedByteArray([Type.OBJ | encoded.type]))
items.append(encoded.bfr)
else:
items.append(PackedByteArray([Type.OBJ | keys.size()]))
for key in keys:
items.append(encode(str(key)))
items.append(encode(value[key]))
return _concat(items)
else:
push_error("Unsupported type: " + str(typeof(value)))
return PackedByteArray()
static func _encode_number(value):
var isInt = typeof(value) == TYPE_INT
#print("compiling value")
#print(value)
if isInt and value >= 0 and value <= 0x0F:
#print("int between 0x0 & 0x0F")
return { "type": 0, "data": PackedByteArray([value]) }
elif isInt and value >= -0x7F and value <= 0x7F:
#print("int between -07F & 0x7F")
return {
"type": Type.MULTI | Type.INT | Type.L8,
"data": PackedByteArray([value])
}
elif isInt and value >= -0x7FFF and value <= 0x7FFF:
#print("int between -07FFF & 0x7FFF")
var bfr = PackedByteArray()
bfr.resize(2)
bfr.encode_s16(0, value)
return {
"type": Type.MULTI | Type.INT | Type.L16,
"data": bfr
}
elif isInt:
#print("int for other cases")
var bfr = PackedByteArray()
bfr.resize(4)
bfr.encode_s32(0, value)
return {
"type": Type.MULTI | Type.INT | Type.L32,
"data": bfr
}
elif typeof(value) == TYPE_FLOAT:
#print("float")
var bfr = PackedByteArray()
bfr.resize(8)
bfr.encode_double(0, value)
return {
"type": Type.MULTI | Type.FLOAT | Type.L64,
"data": bfr
}
push_error("Unsupported number type")
return { "type": 0, "data": PackedByteArray() }
static func _concat(args: Array) -> PackedByteArray:
print("concat", args)
var result = PackedByteArray()
for arr in args:
print("res", arr)
result.append_array(arr)
return result
static func decode(bfr: PackedByteArray) -> Variant:
return decode_with_end(bfr)["value"]
static func decode_with_end(bfr: PackedByteArray) -> Dictionary:
var firstByte = bfr[0]
var type = firstByte & 0xE0
var isMulti = firstByte & 0x10
if type == Type.LIT:
var subtype = firstByte & 0x0F
match subtype:
Type.NULL:
return {"value": null, "end": 1}
Type.UNDEFINED:
return {"value": null, "end": 1}
Type.NAN:
return {"value": float('nan'), "end": 1}
if (firstByte & 0xC0) == 0 && !isMulti:
return {"value": firstByte, "end": 1}
elif (firstByte & 0xC0) == 0 && isMulti:
return _decode_number(firstByte, bfr)
match type:
Type.STR:
if !isMulti:
var len = firstByte & 0x0F
return { "value": bfr.slice(1, len + 1).get_string_from_utf8(), "end": len + 1}
else:
var tmp = _decode_number(firstByte, bfr)
var len = tmp.value
var lenlen = tmp.end
return { "value": bfr.slice(lenlen, lenlen + len).get_string_from_utf8(), "end": lenlen + len}
Type.BOL:
return {"value": bool(firstByte), "end": 1}
Type.ARR:
if !isMulti:
var len = firstByte & 0x0F
var offset = 1
var list = []
for i in len:
var tmp = decode_with_end(bfr.slice(offset))
var value = tmp.value
var newOffset = tmp.end
list.append(value)
offset += newOffset
return {"value": list, "end": offset}
else:
var tmp1 = _decode_number(firstByte, bfr)
var len = tmp1.value
var lenlen = tmp1.end
var offset = lenlen
var list = []
for i in len:
var tmp = decode_with_end(bfr.slice(offset))
var value = tmp.value
var newOffset = tmp.end
list.append(value)
offset += newOffset
return {"value": list, "end": offset}
Type.OBJ:
var dict := {}
if !isMulti:
var len = firstByte & 0x0F
var offset = 1
for i in len:
var tmpA = decode_with_end(bfr.slice(offset))
var key = tmpA.value
var tmpOffset = tmpA.end
var tmpB = decode_with_end(bfr.slice(offset + tmpOffset))
var value = tmpB.value
var newOffset = tmpB.end
dict[key] = value
offset += tmpOffset + newOffset
return {"value": dict, "end": offset}
else:
var tmp = _decode_number(firstByte, bfr)
var len = tmp.value
var lenlen = tmp.end
var offset = lenlen
for i in len:
var tmpA = decode_with_end(bfr.slice(offset))
var key = tmpA.value
var tmpOffset = tmpA.end
var tmpB = decode_with_end(bfr.slice(offset + tmpOffset))
var value = tmpB.value
var newOffset = tmpB.end
dict[key] = value
offset += tmpOffset + newOffset
return {"value": dict, "end": offset}
_:
push_error("Unknown type: " + str(type))
return {"value": null, "end": 0}
return {"value": null, "end": 0}
static func _decode_float(bfr: PackedByteArray, offset: int = 0, bigEndian = false) -> float:
if bigEndian:
return bfr.decode_float(offset)
return PackedByteArray([bfr[offset+3], bfr[offset+2], bfr[offset+1], bfr[offset+0]]).decode_float(0)
static func _decode_number(type: int, bfr: PackedByteArray) -> Dictionary:
var subtype := type & 0x0C
var len := type & 0x03
var bytes := 1
var value
match subtype:
Type.FLOAT:
if len == Type.L32:
#print(bfr.slice(1, 5))
value = _decode_float(bfr, 1)
#print(value)
bytes = 4
elif len == Type.L64:
value = bfr.decode_double(1)
bytes = 8
Type.INT:
if len == Type.L8:
value = bfr.decode_s8(1)
elif len == Type.L16:
value = bfr.decode_s16(1)
bytes = 2
elif len == Type.L32:
value = bfr.decode_s32(1)
bytes = 4
#if value < 0:
#print("possible underflow")
#print(subtype)
#print(len)
#print(value)
return {"value": value, "end": bytes + 1}

View File

@@ -0,0 +1 @@
uid://bxr48ok8lsqt0