initial commit

This commit is contained in:
Lucia Ceionia 2024-11-13 18:30:18 -06:00
commit 52b981e7e7
9 changed files with 666 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
build
Godot/.import
Godot/default_env.tres
Godot/export_presets.cfg
Godot/icon.png
Godot/icon.png.import
Godot/project.godot

View File

@ -0,0 +1,272 @@
extends Reference
var DEBUG: bool = false
enum TAG_TYPE { NULL=0, ROOT=1,
color,
u, s, i, b, center, right,
rainbow, tornado, shake, wave, font
}
const DEFAULT_ALLOWED_TYPES := [TAG_TYPE.color, TAG_TYPE.u, TAG_TYPE.s, TAG_TYPE.b, TAG_TYPE.i]
class BBCodeTag extends Reference:
var inner: Array = []
var tag_type: int = TAG_TYPE.NULL
func parse_junk(junk: String):
return
func _to_string() -> String:
return get_full(TAG_TYPE.values())
func get_full(allowed_types: Array) -> String:
var r := ""
for n in inner:
if n is String: r += n
elif n is BBCodeTag: r += n.get_full(allowed_types)
return r
func get_stripped() -> String:
var r := ""
for n in inner:
if n is String: r += n
elif n is BBCodeTag: r += n.get_stripped()
return r
class BBCodeColorTag extends BBCodeTag:
var color: Color = Color.black
var alpha: bool = false
var color_name: String = ""
# TODO OOP tricks to make this suck less. static
func parse_junk(junk: String):
var junk_regex := RegEx.new()
junk_regex.compile("\\s*=\\s*#([0-9A-Fa-f]{8})|\\s*=\\s*#([0-9A-Fa-f]+)|\\s*=\\s*(\\S*)")
var m := junk_regex.search(junk)
var alpha_col = m.get_string(1)
var code_col = m.get_string(2)
var str_col = m.get_string(3)
if alpha_col:
alpha = true
color = alpha_col
elif code_col:
color = code_col
elif str_col == "transparent":
alpha = true
color = Color.transparent
else:
color_name = str_col
func get_full(allowed_types: Array) -> String:
if TAG_TYPE.color in allowed_types:
var c = color_name if color_name else "#" + color.to_html(alpha)
return "[color=" + c + "]" + .get_full(allowed_types) + "[/color]"
else: return .get_full(allowed_types)
class BBCodeUnsafeTag extends BBCodeTag:
var stuff: String
func parse_junk(junk: String):
stuff = junk
func get_full(allowed_types: Array) -> String:
var tag_str = TAG_TYPE.keys()[tag_type]
if tag_type in allowed_types:
return "[" + tag_str + stuff + "]" + .get_full(allowed_types) + "[/" + tag_str + "]"
else: return .get_full(allowed_types)
class BBCodeSimpleTag extends BBCodeTag:
func get_full(allowed_types: Array) -> String:
var tag_str = TAG_TYPE.keys()[tag_type]
if tag_type in allowed_types:
return "["+tag_str+"]" + .get_full(allowed_types) + "[/" + tag_str + "]"
else: return .get_full(allowed_types)
static func string_to_tag_type(s: String) -> int:
var t: int = TAG_TYPE.NULL
if TAG_TYPE.has(s): t = TAG_TYPE[s]
return t
static func tag_creator(tag_type: int, junk: String) -> BBCodeTag:
var n: BBCodeTag
match tag_type:
TAG_TYPE.color: n = BBCodeColorTag.new()
TAG_TYPE.s, TAG_TYPE.u, TAG_TYPE.i, TAG_TYPE.b,\
TAG_TYPE.center, TAG_TYPE.right: n = BBCodeSimpleTag.new()
TAG_TYPE.rainbow, TAG_TYPE.shake, TAG_TYPE.tornado, TAG_TYPE.wave,\
TAG_TYPE.font:
n = BBCodeUnsafeTag.new()
n.tag_str = TAG_TYPE.keys()[tag_type].to_lower()
_: n = BBCodeTag.new()
n.tag_type = tag_type
if junk != "": n.parse_junk(junk)
return n
# rust rewrite when
var tag_matcher: RegEx = null
func parse_bbcode_text(text: String) -> BBCodeTag:
if DEBUG: print("[BB] processing '" + text + "'")
var bb_root = BBCodeTag.new()
bb_root.tag_type = TAG_TYPE.ROOT
if not tag_matcher:
tag_matcher = RegEx.new()
tag_matcher.compile("(.*?)(\\[(\\w+)([^\\[\\]]*?)\\]|\\[/(\\w+)\\])")
var linear_matches: Array = tag_matcher.search_all(text)
# no tags - plaintext
if linear_matches.empty():
bb_root.inner = [text]
if DEBUG: print("[BB] no tags")
return bb_root
var tag_stack: Array = []
var last_end: int = 0
# loop variables
var end: int
var all: String
var before: String
var whole_tag: String
var tag_open: String
var junk: String
var tag_close: String
var tag: String
var is_close: bool
var tag_type: int
var new_tag: BBCodeTag
var cur_tag: BBCodeTag = bb_root
for m in linear_matches:
if DEBUG: print("[BB MATCH] ", m.strings)
end = m.get_end()
if end != -1: last_end = end
all = m.get_string(0)
before = m.get_string(1)
whole_tag = m.get_string(2)
tag_open = m.get_string(3)
junk = m.get_string(4)
tag_close = m.get_string(5)
is_close = tag_open == ""
tag = tag_close if is_close else tag_open
tag_type = string_to_tag_type(tag)
# add leading text to current tag
cur_tag.inner.push_back(before.replace('[','[lb]'))
# we got a closing tag, unroll the stack
# until we get a matching open or root
if is_close:
while true:
# matching! add text to inner, prev tag is new curr
if cur_tag.tag_type == tag_type:
cur_tag = tag_stack.pop_back()
break
# we're at the root. push as plain text
elif tag_stack.empty():
cur_tag.inner.push_back("[lb]/"+tag+"]")
break
# not matching. go back one on stack and try again
else:
cur_tag = tag_stack.pop_back()
else:
# we got an open tag, make a new tag
new_tag = tag_creator(tag_type, junk)
if DEBUG: print("[BB NEW TAG] " + tag + " " + str(tag_type) + " " + str(new_tag))
# push to the current tag's data
cur_tag.inner.push_back(new_tag)
# push current to stack
tag_stack.push_back(cur_tag)
cur_tag = new_tag
# end parse loop
# end text isn't caught by the regex
if last_end != 0:
var end_str = text.substr(last_end).replace('[','[lb]')
cur_tag.inner.push_back(end_str)
# don't need to unroll stack, we have root
if DEBUG: print("[BB FINAL] ", bb_root)
return bb_root
# this sucks but i need a max_len enforce and am lazy
# TODO rewrite to be better
func parsed_to_text(bbcode: BBCodeTag, allowed_types:Array=DEFAULT_ALLOWED_TYPES, max_len:int=-1) -> String:
if DEBUG: print("[BB parsed_to_text] ",
{"bbcode":bbcode,"allowed_types":allowed_types,"max_len":max_len})
# no length, return full
if max_len == -1:
return bbcode.get_full(allowed_types)
# TODO strip better
var result: String = bbcode.get_full(allowed_types)
if result.length() > max_len:
result = bbcode.get_stripped().left(max_len)
if DEBUG: print("[BB parsed_to_text] ", result)
return result
static func clamp_alpha(bbcode: BBCodeTag, min_alpha: float):
if bbcode is BBCodeColorTag:
if bbcode.alpha: bbcode.color.a = max(bbcode.color.a, min_alpha)
for n in bbcode.inner: if n is BBCodeTag: clamp_alpha(n, min_alpha)
static func apply_allowed(bbcode: BBCodeTag, allowed_tags: Array):
if not bbcode.tag_type in allowed_tags and bbcode.tag_type != TAG_TYPE.ROOT:
bbcode.tag_type = TAG_TYPE.NULL
for n in bbcode.inner: if n is BBCodeTag: apply_allowed(n, allowed_tags)
func replace_in_strings(bbcode: BBCodeTag, find: String, replace) -> bool:
var l: int
for i in bbcode.inner.size():
if bbcode.inner[i] is String:
l = bbcode.inner[i].find(find)
if l != -1:
if DEBUG: print("[BB REPLACE] ", {"l":l,"i":i,"bbcode.inner":bbcode.inner})
var b = bbcode.inner[i].substr(0,l)
var a = bbcode.inner[i].substr(l+find.length())
bbcode.inner[i] = b
bbcode.inner.insert(i+1,replace)
bbcode.inner.insert(i+2,a)
if DEBUG: print("[BB REPLACE] ", {"b":b,"replace":replace,"a":a,"inner":bbcode.inner})
return true
elif bbcode.inner[i] is BBCodeTag:
if replace_in_strings(bbcode.inner[i], find, replace): return true
return false
static func find_in_strings(bbcode: BBCodeTag, find: String) -> bool:
for n in bbcode.inner:
if n is String:
if find in n: return true
elif n is BBCodeTag:
if find_in_strings(n, find): return true
return false
func test():
var tests := [
"foo bar",
"foo [u]foobar[/u] bar",
"foo [color=red]foobar[/u] bar",
"foo [color=#10ffffff]foobar[/u] bar",
"foo [color=#ffffff]foobar[/u] bar",
"foo [color=#1111111111111]foobar[/u] bar",
"foo [color=transparent]foobar[/u] bar",
"foo [invalid]foobar[/u] bar",
"foo [NULL]foobar[/u] bar",
"foo [ROOT]foobar[/u] bar",
"foo [u][s][u]foo[/u]bar[/s] bar[/u]",
"[color]foo [u][s][u]foo[/u]bar[/s] bar[/u]",
"foo [u][s][u]foo[/u]bar[/u] bar[/u]",
"[wave amp=8]test",
"foo [u][s][u]foo[/u]bar[/s] [rainbow]bar[/u]",
"[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a[u]a",
"[u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u][u]a[/u]",
]
for test in tests:
print("[BB TEST] ", test)
var r := parse_bbcode_text(test)
print("[BB TEST FULL DEFAULT] ", r.get_full(DEFAULT_ALLOWED_TYPES))
print("[BB TEST FULL ALL] ", r.get_full(TAG_TYPE.values()))
print("[BB TEST U] ", r.get_full([TAG_TYPE.u]))
print("[BB TEST STRIPPED] ", r.get_stripped())
print("[BB TEST LEN 10] ", parsed_to_text(r, DEFAULT_ALLOWED_TYPES, 10))
clamp_alpha(r, 0.5)
print("[BB TEST ALPHA] ", r.get_full([TAG_TYPE.color]))

193
Godot/mods/LucysLib/main.gd Normal file
View File

@ -0,0 +1,193 @@
extends Node
const NetManager_t := preload("res://mods/LucysLib/net.gd")
const BBCode_t := preload("res://mods/LucysLib/bbcode.gd")
var NetManager: NetManager_t
var BBCode: BBCode_t
var ALLOWED_TAG_TYPES: Array = BBCode_t.DEFAULT_ALLOWED_TYPES
var LOG_MESSAGES: bool = false
var DEBUG: bool = false
# no setting from outside
var HAS_BB_MSG: bool = false setget set_hbbmsg
var HAS_LOG_MSG: bool = false setget set_hlogmsg
func set_hbbmsg(val): pass
func set_hlogmsg(val): pass
func _enter_tree():
NetManager = NetManager_t.new()
BBCode = BBCode_t.new()
#NetManager.DEBUG = true
#BBCode.DEBUG = true
func _ready():
print("[LUCYSLIB] LucysLib 0.1.0 ready")
#BBCode.test()
func register_bb_msg_support():
if HAS_BB_MSG: return
print("[LUCYSLIB] registering bbcode message receive support...")
NetManager.add_network_processor("message", funcref(self, "process_packet_message"), 100)
HAS_BB_MSG = true
func register_log_msg_support():
if HAS_LOG_MSG: return
print("[LUCYSLIB] registering log message support...")
NetManager.add_network_processor("message", funcref(self, "process_packet_message_log"), -100)
HAS_LOG_MSG = true
# future use
func process_packet_lucy_packet(DATA, PACKET_SENDER, from_host) -> bool:
print("[LUCY PACKET] [" + PACKET_SENDER + " " + Network._get_username_from_id(PACKET_SENDER) + "]")
return true
# message logging
func process_packet_message_log(DATA, PACKET_SENDER, from_host) -> bool:
if LOG_MESSAGES:
print("[MSG] [" + PACKET_SENDER + " " + Network._get_username_from_id(PACKET_SENDER) + "] " + DATA)
return false
# bbcode support in messages
func process_packet_message(DATA, PACKET_SENDER, from_host) -> bool:
var has_bb := true
if not Network._validate_packet_information(DATA,
["message", "color", "local", "position", "zone", "zone_owner", "bb_user", "bb_msg"],
[TYPE_STRING, TYPE_STRING, TYPE_BOOL, TYPE_VECTOR3, TYPE_STRING, TYPE_INT, TYPE_STRING, TYPE_STRING]):
has_bb = false
if not Network._validate_packet_information(DATA,
["message", "color", "local", "position", "zone", "zone_owner"],
[TYPE_STRING, TYPE_STRING, TYPE_BOOL, TYPE_VECTOR3, TYPE_STRING, TYPE_INT]):
return true
if PlayerData.players_muted.has(PACKET_SENDER) or PlayerData.players_hidden.has(PACKET_SENDER):
return false
if not Network._message_cap(PACKET_SENDER): return false
var user_id: int = PACKET_SENDER
# this is gonna become a real color anyway but...
# sure, the vanilla escaping is *totally* necessary
var user_color: String = DATA["color"].left(12).replace('[','')
var user_message: String = DATA["message"]
var bb_user: String = ""
var bb_msg: String = ""
if has_bb:
bb_user = DATA["bb_user"]
bb_msg = DATA["bb_msg"]
if not DATA["local"]:
receive_safe_message(user_id, user_color, user_message, false, bb_msg, bb_user)
else :
var dist = DATA["position"].distance_to(Network.MESSAGE_ORIGIN)
if DATA["zone"] == Network.MESSAGE_ZONE and DATA["zone_owner"] == PlayerData.player_saved_zone_owner:
if dist < 25.0:
receive_safe_message(user_id, user_color, user_message, true, bb_msg, bb_user)
# don't process it again!
return true
func send_message(message: BBCode_t.BBCodeTag, color: Color, local: bool = false,
custom_name: BBCode_t.BBCodeTag = null, to: String = "peers"):
if not message: return
if not Network._message_cap(Network.STEAM_ID):
Network._update_chat("Sending too many messages too quickly!", false)
Network._update_chat("Sending too many messages too quickly!", true)
return
var is_host: bool = Network.GAME_MASTER or Network.PLAYING_OFFLINE
if DEBUG:
var thing = {"message": message, "color": color, "local": local,
"custom_name": custom_name, "to": to, "is_host": is_host,
"ALLOWED_TAG_TYPES": ALLOWED_TAG_TYPES}
print("[LUCYSLIB send_message] ", thing)
var bb_user: String = ""
var bb_msg: String = ""
var default_msg: String = ""
var color_str: String = ""
var net_name: String = Network.STEAM_USERNAME.replace('[','').replace(']','')
# verify alpha & name present
if not is_host:
BBCode.clamp_alpha(message, 0.7)
if custom_name:
BBCode.clamp_alpha(custom_name, 0.7)
if custom_name.get_stripped() != net_name:
custom_name = null
if not BBCode_t.find_in_strings(message,'%u'):
message.inner.push_front('%u ')
color.a = max(color.a, 0.7)
# construct message
if custom_name: bb_user = BBCode.parsed_to_text(custom_name, ALLOWED_TAG_TYPES, 200)
bb_msg = BBCode.parsed_to_text(message, ALLOWED_TAG_TYPES, 500)
default_msg = message.get_stripped().left(500)
color_str = color.to_html(true)
var msg_pos = Network.MESSAGE_ORIGIN.round()
receive_safe_message(Network.STEAM_ID, color_str, default_msg, local,
bb_msg, bb_user)
Network._send_P2P_Packet(
{"type": "message", "message": default_msg, "color": color_str, "local": local,
"position": Network.MESSAGE_ORIGIN, "zone": Network.MESSAGE_ZONE,
"zone_owner": PlayerData.player_saved_zone_owner,
"bb_user": bb_user, "bb_msg": bb_msg},
to, 2, Network.CHANNELS.GAME_STATE)
var _rsm_color_regex: RegEx = null
func _rsm_construct(user_id: int, color: String, message: String, local: bool,
bb_msg: String, bb_user: String, srv_msg: bool) -> BBCode_t.BBCodeTag:
var net_name: String = Network._get_username_from_id(user_id).replace('[','').replace(']','')
var name := BBCode.parse_bbcode_text(net_name)
if bb_user != "":
if not srv_msg:
# check that name matches net name & clamp alpha
var user_parse := BBCode.parse_bbcode_text(bb_user)
BBCode_t.clamp_alpha(user_parse, 0.7)
if user_parse.get_stripped() == net_name:
name = user_parse
else:
name = BBCode.parse_bbcode_text(bb_user)
var to_parse = bb_msg if bb_msg != "" else message
if not "%u" in to_parse.left(32) and not srv_msg:
to_parse = "%u " + to_parse
var parsed_msg := BBCode.parse_bbcode_text(to_parse)
# make a node with the user's color and name
var real_color: Color = color
if not srv_msg: real_color.a = max(real_color.a, 0.7)
var color_node: BBCode_t.BBCodeColorTag = BBCode_t.tag_creator(BBCode_t.TAG_TYPE.color,"")
color_node.color = real_color
color_node.inner = [name]
BBCode.replace_in_strings(parsed_msg,"%u",color_node)
return parsed_msg
func receive_safe_message(user_id: int, color: String, message: String, local: bool = false,
bb_msg: String = "", bb_user: String = ""):
# we don't need to check as much stuff from ourselves or host
var srv_msg: bool = user_id == Network.STEAM_ID or user_id == Steam.getLobbyOwner(Network.STEAM_LOBBY_ID)
if DEBUG:
var thing = {"user_id": user_id, "color": color, "message":message, "local": local,
"bb_msg": bb_msg, "bb_user": bb_user, "srv_msg": srv_msg}
print("[LUCYSLIB receive_safe_message] ", thing)
# lol lmao it barely even works in vanilla. idc so i'm not rewriting it
if OptionsMenu.chat_filter:
message = SwearFilter._filter_string(message)
bb_msg = SwearFilter._filter_string(bb_msg)
# parse
var parsed_msg := _rsm_construct(user_id, color, message, local, bb_msg, bb_user, srv_msg)
# stringify
var final_message = BBCode.parsed_to_text(parsed_msg, ALLOWED_TAG_TYPES, 512)
Network._update_chat(final_message, local)

View File

@ -0,0 +1,59 @@
extends Reference
const PriorityFuncRef := preload("res://mods/LucysLib/priorityfuncref.gd")
var DEBUG: bool = false
var network_processors: Dictionary = {}
func add_network_processor(packet_type: String, function: FuncRef, priority: int = 0) -> bool:
if not packet_type: return false
if not function: return false
if not network_processors.has(packet_type):
network_processors[packet_type] = []
if function != null:
var pf: PriorityFuncRef = PriorityFuncRef.new(function, priority)
var arr: Array = network_processors[packet_type]
arr.append(pf)
arr.sort_custom(PriorityFuncRef, "sort")
if DEBUG:
print("[LUCYSLIB NET] Added Network Processor for '" + packet_type + "': " + str(pf) + " new list: " + str(arr))
return true
return false
func clean_processor_arr(processors: Array) -> Array:
var n: Array = []
var pf: PriorityFuncRef
for p in processors:
pf = p
if pf.function.is_valid():
n.append(pf)
return n
func process_packet(DATA, PACKET_SENDER, from_host) -> bool:
if not network_processors.has(DATA["type"]): return false
var processors: Array = network_processors[DATA["type"]]
if DEBUG:
print("[LUCYSLIB NET] Running processors for ", DATA["type"], ": ", processors)
var pf: PriorityFuncRef
var reject: bool = false
var do_cleanup: bool = false
for p in processors:
# i want static types
pf = p
# call func if it exists
if pf.function.is_valid():
if DEBUG:
print("[LUCYSLIB NET] processor ", pf, "...")
reject = pf.function.call_func(DATA, PACKET_SENDER, from_host)
if DEBUG:
print("[LUCYSLIB NET] processor ", pf, " rejected packet: ", reject)
else: do_cleanup = true
# function consumed packet, no more processors
if reject: break
if do_cleanup:
network_processors[DATA["type"]] = clean_processor_arr(processors)
return reject

View File

@ -0,0 +1,14 @@
extends Reference
var function: FuncRef
var priority: int
func _init(f: FuncRef, p: int = 0):
function = f
priority = p
func _to_string():
return "(" + str(priority) + "," + function.function + ")"
static func sort(a, b):
return a.priority < b.priority

7
LucysLib/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.idea/
.vs/
*.user
/local
Makefile
bin/
obj/

17
LucysLib/LucysLib.csproj Normal file
View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblySearchPaths>$(AssemblySearchPaths);$(GDWeavePath)/core</AssemblySearchPaths>
</PropertyGroup>
<ItemGroup>
<Reference Include="GDWeave" Private="false"/>
<Reference Include="Serilog" Private="false"/>
</ItemGroup>
<ItemGroup>
<None Include="manifest.json" CopyToOutputDirectory="PreserveNewest"/>
</ItemGroup>
</Project>

92
LucysLib/Mod.cs Normal file
View File

@ -0,0 +1,92 @@
using GDWeave;
using GDWeave.Godot;
using GDWeave.Godot.Variants;
using GDWeave.Modding;
namespace LucysLib;
public class Mod : IMod {
public static IModInterface ModInterface;
public Mod(IModInterface modInterface) {
modInterface.Logger.Information("Lucy was here :3");
ModInterface = modInterface;
modInterface.RegisterScriptMod(new LucysNetFixes());
}
public void Dispose(){}
}
public record CodeChange {
public required String name;
public required Func<Token, bool>[] multitoken_prefix;
public required Token[] code_to_add;
}
public class LucysNetFixes : IScriptMod {
bool IScriptMod.ShouldRun(string path) => path == "res://Scenes/Singletons/SteamNetwork.gdc";
CodeChange[] changes = {
new CodeChange {
name = "read packet intercept",
// FLUSH_PACKET_INFORMATION[PACKET_SENDER] += 1
// END
multitoken_prefix = new Func<Token, bool>[] {
t => t is IdentifierToken {Name: "FLUSH_PACKET_INFORMATION"},
t => t.Type == TokenType.BracketOpen,
t => t is IdentifierToken {Name: "PACKET_SENDER"},
t => t.Type == TokenType.BracketClose,
t => t.Type == TokenType.OpAssignAdd,
t => t is ConstantToken {Value:IntVariant{Value: 1}},
t => t.Type == TokenType.Newline,
},
// if $"/root/LucysLib".NetManager.process_packet(DATA, PACKET_SENDER, from_host): return
// END
code_to_add = new Token[] {
new Token(TokenType.CfIf),
new Token(TokenType.Dollar),
new ConstantToken(new StringVariant("/root/LucysLib")),
new Token(TokenType.Period),
new IdentifierToken("NetManager"),
new Token(TokenType.Period),
new IdentifierToken("process_packet"),
new Token(TokenType.ParenthesisOpen),
new IdentifierToken("DATA"),
new Token(TokenType.Comma),
new IdentifierToken("PACKET_SENDER"),
new Token(TokenType.Comma),
new IdentifierToken("from_host"),
new Token(TokenType.ParenthesisClose),
new Token(TokenType.Colon),
new Token(TokenType.CfReturn),
new Token(TokenType.Newline, 2),
}
},
};
IEnumerable<Token> IScriptMod.Modify(string path, IEnumerable<Token> tokens)
{
var pending_changes = changes
.Select(c => (c, new MultiTokenWaiter(c.multitoken_prefix)))
.ToList();
// I'm sure there's a better way to do this
// with list comprehension stuff, but my
// C# is too rusty
foreach (var token in tokens) {
var had_change = false;
foreach (var (change, waiter) in pending_changes) {
if (waiter.Check(token)) {
Mod.ModInterface.Logger.Information($"Adding LucysLib Network mod {change.name}");
yield return token;
foreach (var t in change.code_to_add) yield return t;
had_change = true;
break;
}
}
if (!had_change) yield return token;
}
}
}

5
LucysLib/manifest.json Normal file
View File

@ -0,0 +1,5 @@
{
"Id": "LucysLib",
"AssemblyPath": "LucysLib.dll",
"PackPath": "LucysLib.pck"
}