initial commit
This commit is contained in:
commit
52b981e7e7
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal 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
|
272
Godot/mods/LucysLib/bbcode.gd
Normal file
272
Godot/mods/LucysLib/bbcode.gd
Normal 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
193
Godot/mods/LucysLib/main.gd
Normal 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)
|
59
Godot/mods/LucysLib/net.gd
Normal file
59
Godot/mods/LucysLib/net.gd
Normal 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
|
14
Godot/mods/LucysLib/priorityfuncref.gd
Normal file
14
Godot/mods/LucysLib/priorityfuncref.gd
Normal 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
7
LucysLib/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.idea/
|
||||||
|
.vs/
|
||||||
|
*.user
|
||||||
|
/local
|
||||||
|
Makefile
|
||||||
|
bin/
|
||||||
|
obj/
|
17
LucysLib/LucysLib.csproj
Normal file
17
LucysLib/LucysLib.csproj
Normal 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
92
LucysLib/Mod.cs
Normal 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
5
LucysLib/manifest.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"Id": "LucysLib",
|
||||||
|
"AssemblyPath": "LucysLib.dll",
|
||||||
|
"PackPath": "LucysLib.pck"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user