mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-12-07 13:49:46 +00:00
remove tangled update egui add my script for components, fix clippy
This commit is contained in:
parent
63d12a5448
commit
7df0b18aae
27 changed files with 1524 additions and 2708 deletions
361
ComponentVftable.py
Normal file
361
ComponentVftable.py
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
# TODO write a description for this script
|
||||
# @author
|
||||
# @category _Custom
|
||||
# @keybinding
|
||||
# @menupath
|
||||
# @toolbar
|
||||
|
||||
import ghidra
|
||||
from ghidra.app.decompiler.flatapi import FlatDecompilerAPI
|
||||
from ghidra.app.script import GhidraState
|
||||
from ghidra.app.util.cparser.C import CParser
|
||||
from ghidra.program.flatapi import FlatProgramAPI
|
||||
from ghidra.program.model.address import Address
|
||||
from ghidra.program.model.data import (
|
||||
ArrayDataType,
|
||||
DataTypeConflictHandler,
|
||||
DataTypeManager,
|
||||
StringDataType,
|
||||
StructureDataType,
|
||||
CategoryPath,
|
||||
)
|
||||
from ghidra.program.model.listing import Program
|
||||
|
||||
|
||||
def get_state():
|
||||
# type: () -> GhidraState
|
||||
return getState()
|
||||
|
||||
|
||||
state = get_state()
|
||||
program = state.getCurrentProgram()
|
||||
|
||||
fpapi = FlatProgramAPI(program)
|
||||
|
||||
fdapi = FlatDecompilerAPI(fpapi)
|
||||
|
||||
|
||||
def hex_n(n):
|
||||
if n[0:2] == "0x":
|
||||
num = int(n, 16)
|
||||
else:
|
||||
num = int(n)
|
||||
return num
|
||||
|
||||
|
||||
type_defs = {
|
||||
"int32": "int",
|
||||
"uint32": "uint",
|
||||
"uint32_t": "uint",
|
||||
"unsigned int": "uint",
|
||||
"int64": "longlong",
|
||||
"uint64": "ulonglong",
|
||||
"std::string": "StdString",
|
||||
"EntityID": "int",
|
||||
"int16": "short",
|
||||
"uint16": "ushort",
|
||||
"GAME_EFFECT::Enum": "GameEffect",
|
||||
"b2ObjectID": "b2Object*",
|
||||
|
||||
"vec2": "Vec2",
|
||||
"LensValue<float>": "LensValueFloat",
|
||||
"LensValue<int>": "LensValueInt",
|
||||
"LensValue<bool>": "LensValueBool",
|
||||
"MAP_STRING_STRING": "MapStringString",
|
||||
"VEC_PENDINGPORTAL": "VecPendingPortal",
|
||||
"VECTOR_INT32": "VectorInt32",
|
||||
"VEC_NPCPARTY": "VecNpcParty",
|
||||
"VECTOR_STRING": "VectorString",
|
||||
"VEC_CUTTHROUGHWORLD": "VecCutThroughWorld",
|
||||
"grid::ICell": "Cell",
|
||||
"VERLET_TYPE::Enum": "VerletType",
|
||||
"UintArrayInline": "UintArrayInline",
|
||||
"FloatArrayInline": "FloatArrayInline",
|
||||
"Vec2ArrayInline": "Vec2ArrayInline",
|
||||
"VerletLinkArrayInline": "VerletLinkArrayInline",
|
||||
"VerletSprite": "VerletSprite",
|
||||
"ivec2": "Vec2i",
|
||||
"EntityID": "int",
|
||||
"types::aabb": "AABB",
|
||||
"ENTITY_VEC": "VecInt",
|
||||
"TeleportComponentState::Enum": "TeleportComponentState",
|
||||
"VEC_OF_MATERIALS": "VecMaterials",
|
||||
"VECTOR_FLOAT": "VecFloat",
|
||||
"VirtualTextureHandle": "VirtualTextureHandle",
|
||||
"SpriteStainsState": "SpriteStainsState",
|
||||
"SpriteStains": "SpriteStains",
|
||||
"ValueRange": "ValueRange",
|
||||
"types::fcolor": "Color",
|
||||
"as::Sprite": "Sprite",
|
||||
"SpriteRenderList": "SpriteRenderList",
|
||||
"STACK_ANIMATIONSTATE": "StackAnimationState",
|
||||
"ComponentTags": "ComponentTags",
|
||||
"StatusEffectType": "StatusEffectType",
|
||||
"ProjectileTriggers": "ProjectileTriggers",
|
||||
"VEC_ENTITY": "VecInt",
|
||||
"EntityTypeID": "EntityTypeID",
|
||||
"RAGDOLL_FX::Enum": "RagdollFx",
|
||||
"PROJECTILE_TYPE::Enum": "ProjectileType",
|
||||
"ConfigGunActionInfo": "ConfigGunActionInfo",
|
||||
"ConfigExplosion": "ConfigExplosion",
|
||||
"ConfigDamagesByType": "ConfigDamagesByType",
|
||||
"ConfigDamageCritical": "ConfigDamageCritical",
|
||||
"PixelSprite": "PixelSprite",
|
||||
"std::vector<b2Body*>*": "VecPtrB2Body",
|
||||
"b2WeldJoint": "b2WeldJoint",
|
||||
"types::xform": "xform",
|
||||
"JOINT_TYPE::Enum": "JointType",
|
||||
"b2Joint": "b2Joint",
|
||||
"b2Vec2": "b2Vec2",
|
||||
"b2Body": "b2Body",
|
||||
"PathFindingNodeHandle": "PathFindingNodeHandle",
|
||||
"VECTOR_PATHNODE": "VECTOR_PATHNODE",
|
||||
"PathFindingComponentState::Enum": "PathFindingComponentState",
|
||||
"PathFindingLogic": "PathFindingLogic",
|
||||
"MSG_QUEUE_PATH_FINDING_RESULT": "MsgQueuePathFindingResult",
|
||||
"PathFindingInput": "PathFindingInput",
|
||||
"ParticleEmitter_Animation": "ParticleEmitterAnimation",
|
||||
"PARTICLE_EMITTER_CUSTOM_STYLE::Enum": "ParticleEmitterCustomStyle",
|
||||
"NINJA_ROPE_SEGMENT_VECTOR": "VecNinjaRopeSegment",
|
||||
"MOVETOSURFACE_TYPE::Enum": "MoveToSurfaceType",
|
||||
"types::iaabb": "IAABB",
|
||||
"MATERIAL_VEC_DOUBLES": "VecDouble",
|
||||
"std::vector<int>": "VecInt",
|
||||
"LuaManager": "LuaManager",
|
||||
"ValueMap": "MapValue",
|
||||
"LUA_VM_TYPE::Enum": "LuaVmType",
|
||||
"ValueRangeInt": "ValueRangeInt",
|
||||
"ConfigLaser": "ConfigLaser",
|
||||
"INVENTORY_KIND::Enum": "InventoryKind",
|
||||
"ImGuiContext": "ImGuiContext",
|
||||
"INVENTORYITEM_VECTOR": "VecInventoryItem",
|
||||
"InvenentoryUpdateListener": "InvenentoryUpdateListener",
|
||||
"IKLimbStateVec": "IKLimbStateVec",
|
||||
"IKLimbAttackerState": "IKLimbAttackerState",
|
||||
"HIT_EFFECT::Enum": "HitEffect",
|
||||
"VISITED_VEC": "VecVisited",
|
||||
"USTRING": "UString",
|
||||
"VECTOR_STR": "VecStr",
|
||||
"VECTOR_ENTITYID": "VecInt",
|
||||
"EXPLOSION_TRIGGER_TYPE::Enum": "ExplosionTriggerType",
|
||||
"ConfigDrugFx": "ConfigDrugFx",
|
||||
"std::vector<int>": "StdVecInt",
|
||||
"std::vector<float>": "StdVecFloat",
|
||||
"CharacterStatsModifier": "CharacterStatsModifier",
|
||||
"AudioSourceHandle": "AudioSourceHandle",
|
||||
"DAMAGE_TYPES::Enum": "DamageTypes",
|
||||
"ARC_TYPE::Enum": "ArcType",
|
||||
"RtsUnitGoal": "RtsUnitGoal",
|
||||
"AI_STATE_STACK": "AIStateStack",
|
||||
"EntityTags": "EntityTags",
|
||||
"AIData": "AIData",
|
||||
"ceng::CArray2D<uint32>": "CArray2DUint32",
|
||||
}
|
||||
|
||||
|
||||
def get_types_file():
|
||||
file = askFile("Component Docs", "Approve").getAbsolutePath()
|
||||
content = open(file, "r").read()
|
||||
lines = content.replace("\r", "").split("\n")
|
||||
name = ""
|
||||
content = {
|
||||
"ParticleEmitterComponent": {
|
||||
"custom_style": "PARTICLE_EMITTER_CUSTOM_STYLE::Enum",
|
||||
"m_cached_image_animation": "ParticleEmitter_Animation*",
|
||||
},
|
||||
"ExplosionComponent": {"trigger": "EXPLOSION_TRIGGER_TYPE::Enum"},
|
||||
"InventoryComponent": {"update_listener": "InvenentoryUpdateListener*"},
|
||||
"PathFindingComponent": {
|
||||
"job_result_receiver": "MSG_QUEUE_PATH_FINDING_RESULT"
|
||||
},
|
||||
}
|
||||
for line in lines:
|
||||
if line == "":
|
||||
continue
|
||||
if line[0] != " ":
|
||||
name = line
|
||||
if name not in content.keys():
|
||||
content[name] = {}
|
||||
continue
|
||||
if line[1] == "-":
|
||||
continue
|
||||
parts = [p for p in line.strip().split(" ") if p != ""]
|
||||
ty = parts[0]
|
||||
field = parts[1]
|
||||
if ty in type_defs.keys():
|
||||
ty = type_defs[ty]
|
||||
content[name][field] = (ty, line[125:].replace('"', ""))
|
||||
return content
|
||||
|
||||
|
||||
def do_vftable(addr, content, name):
|
||||
ref = [x.getFromAddress() for x in fpapi.getReferencesTo(addr)][0]
|
||||
fun = fpapi.getFunctionContaining(ref)
|
||||
super_parents = [
|
||||
fpapi.getFunctionContaining(x.getFromAddress())
|
||||
for x in fpapi.getReferencesTo(fun.getEntryPoint())
|
||||
]
|
||||
size = None
|
||||
for super_parent in super_parents:
|
||||
super_parent_decomp = fdapi.decompile(super_parent)
|
||||
if "operator_new(" not in super_parent_decomp:
|
||||
continue
|
||||
if size is not None:
|
||||
continue
|
||||
size = hex_n(super_parent_decomp.split("operator_new(")[1].split(")")[0])
|
||||
|
||||
parent = fdapi.decompile(fun)
|
||||
derived_size = False
|
||||
if size is None:
|
||||
if "operator_new(" in parent:
|
||||
size = hex_n(parent.split("operator_new(")[1].split(")")[0])
|
||||
else:
|
||||
derived_size = True
|
||||
size = 0x48
|
||||
new_addr = addr.add(14 * 4)
|
||||
v = hex(fpapi.getInt(new_addr))
|
||||
deref = fpapi.getAddressFactory().getAddress(v)
|
||||
decompiled = fdapi.decompile(fpapi.getFunctionAt(deref))
|
||||
things = []
|
||||
while True:
|
||||
data = {}
|
||||
found = decompiled.find('"')
|
||||
decompiled = decompiled[found + 1 :]
|
||||
if found == -1:
|
||||
break
|
||||
close = decompiled.find('"')
|
||||
if close == -1:
|
||||
print("no end found!")
|
||||
break
|
||||
if "{" not in decompiled[close + 1 :]:
|
||||
break
|
||||
data["field"] = str(decompiled[:close])
|
||||
decompiled = decompiled[close + 1 :]
|
||||
lines = decompiled.split("}")[0].split("{")[1].split("\n")
|
||||
for line in lines:
|
||||
if "+" in line:
|
||||
add = line.find("+")
|
||||
line = line[add + 2 :]
|
||||
num = line[:-1]
|
||||
num = hex_n(num)
|
||||
data["offset"] = num
|
||||
if "[2]" in line:
|
||||
assign = line.find("=")
|
||||
line = line[assign + 2 :]
|
||||
semi = line.find(";")
|
||||
num = line[:semi]
|
||||
if num.startswith("0x"):
|
||||
num = hex_n(num)
|
||||
else:
|
||||
num = int(num)
|
||||
data["size"] = num
|
||||
if derived_size:
|
||||
size = max(size, num)
|
||||
if "offset" in data:
|
||||
if "size" not in data:
|
||||
line = decompiled.split("\n")[1]
|
||||
if "[2]" in line:
|
||||
assign = line.find("=")
|
||||
line = line[assign + 2 :]
|
||||
semi = line.find(";")
|
||||
num = line[:semi]
|
||||
if num.startswith("0x"):
|
||||
num = hex_n(num)
|
||||
else:
|
||||
num = int(num)
|
||||
data["size"] = num
|
||||
if derived_size:
|
||||
size = max(size, num)
|
||||
else:
|
||||
data["size"] = 1
|
||||
if derived_size:
|
||||
size = max(size, 1)
|
||||
things.append(data)
|
||||
fields = content[name]
|
||||
for thing in things:
|
||||
thing["type"] = fields[thing["field"]][0]
|
||||
thing["comment"] = fields[thing["field"]][1]
|
||||
return things, size
|
||||
|
||||
|
||||
def create_type(dtm, name, size):
|
||||
existing = dtm.getDataType("noita.exe/" + name)
|
||||
if existing is None:
|
||||
category = CategoryPath("/noita.exe")
|
||||
struct = StructureDataType(category, name, size)
|
||||
struct = dtm.addDataType(struct, DataTypeConflictHandler.REPLACE_HANDLER)
|
||||
else:
|
||||
struct = existing
|
||||
return struct
|
||||
|
||||
def construct_structs(defs, name, size):
|
||||
data_type_manager = program.getDataTypeManager()
|
||||
# NOTE: hax here
|
||||
print(name, size)
|
||||
|
||||
struct = StructureDataType(name, size)
|
||||
struct.replaceAtOffset(
|
||||
0,
|
||||
create_type(data_type_manager, "Component", 0x48),
|
||||
0x48,
|
||||
"inherited_fields",
|
||||
"",
|
||||
)
|
||||
defs.sort(lambda x, y: x["offset"] > y["offset"])
|
||||
for thing in defs:
|
||||
ptr = False
|
||||
if thing["type"][-1] == "*":
|
||||
ptr = True
|
||||
thing["type"] = thing["type"][:-1]
|
||||
ty = data_type_manager.getDataType("/" + thing["type"])
|
||||
if ty is None:
|
||||
ty = create_type(data_type_manager, thing["type"], thing["size"])
|
||||
if ty is None:
|
||||
print("cant find: " + thing["type"] + " " + str(thing["size"]))
|
||||
ty = ArrayDataType(
|
||||
data_type_manager.getDataType("/undefined1"), thing["size"], 1
|
||||
)
|
||||
if ptr:
|
||||
ty = data_type_manager.getPointer(ty)
|
||||
struct.replaceAtOffset(
|
||||
thing["offset"], ty, thing["size"], thing["field"], thing["comment"]
|
||||
)
|
||||
data_type_manager.addDataType(struct, DataTypeConflictHandler.REPLACE_HANDLER)
|
||||
|
||||
# ty = data_type_manager.getDataType("/" + )
|
||||
# parser = CParser(data_type_manager)
|
||||
# parsed_datatype = parser.parse(struct_str)
|
||||
# data_type_manager.addDataType(
|
||||
# parsed_datatype, DataTypeConflictHandler.DEFAULT_HANDLER
|
||||
# )
|
||||
|
||||
|
||||
def get_all():
|
||||
table = program.getSymbolTable()
|
||||
addrs = []
|
||||
for i in table.getClassNamespaces():
|
||||
search = "Component"
|
||||
n = i.name
|
||||
if n[-len(search) :] != search or n == search:
|
||||
continue
|
||||
for s in table.getChildren(i.symbol):
|
||||
if s.name != "vftable":
|
||||
continue
|
||||
print(n)
|
||||
print(s.address)
|
||||
# NOTE: hax here
|
||||
addrs.append((s.address, n))
|
||||
return addrs
|
||||
|
||||
|
||||
content = get_types_file()
|
||||
|
||||
|
||||
def do(pair):
|
||||
if pair[1] in content:
|
||||
things, size = do_vftable(pair[0], content, pair[1])
|
||||
construct_structs(things, pair[1], size)
|
||||
|
||||
|
||||
# do_vftable(currentAddress, content, "AIAttackComponent")
|
||||
[do(x) for x in get_all()]
|
||||
1021
blob_guy/Cargo.lock
generated
1021
blob_guy/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -25,4 +25,4 @@ rayon = "1.11.0"
|
|||
|
||||
[dev-dependencies]
|
||||
rupl = {git = "https://github.com/bgkillas/rupl.git", default-features = false, features = ["egui"] }
|
||||
eframe = "0.32.1"
|
||||
eframe = "0.33.0"
|
||||
|
|
|
|||
173
ewext/Cargo.lock
generated
173
ewext/Cargo.lock
generated
|
|
@ -52,15 +52,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.2"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
|
|
@ -128,9 +128,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
|
|
@ -138,21 +138,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.30.5"
|
||||
version = "0.30.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85"
|
||||
checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
|
|
@ -189,25 +189,25 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.175"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
|
|
@ -216,6 +216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -279,18 +280,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
|
@ -373,18 +374,28 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -393,14 +404,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.143"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -413,6 +425,12 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
|
|
@ -442,9 +460,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -484,116 +502,51 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "2.1.1"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56"
|
||||
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.3+wasi-0.2.4"
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.45.0"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.26"
|
||||
version = "0.8.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.26"
|
||||
version = "0.8.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ opt-level = 3
|
|||
eyre = "0.6.12"
|
||||
noita_api = {path = "../noita_api"}
|
||||
shared = {path = "../shared"}
|
||||
libloading = "0.8.8"
|
||||
libloading = "0.8.9"
|
||||
rand = "0.9.2"
|
||||
rustc-hash = "2.1.1"
|
||||
bimap = "0.6.3"
|
||||
|
|
|
|||
982
noita-proxy/Cargo.lock
generated
982
noita-proxy/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,3 @@
|
|||
[workspace]
|
||||
members = ["tangled"]
|
||||
resolver = "2"
|
||||
|
||||
[package]
|
||||
name = "noita-proxy"
|
||||
description = "Noita Entangled Worlds proxy application."
|
||||
|
|
@ -11,32 +7,32 @@ edition = "2024"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
eframe = { version= "0.32.1", features = ["glow", "default_fonts", "wayland", "x11"], default-features = false }
|
||||
eframe = { version= "0.33.0", features = ["glow", "default_fonts", "wayland", "x11"], default-features = false }
|
||||
rfd = "0.15.4"
|
||||
egui_extras = { version = "0.32.1", features = ["all_loaders"] }
|
||||
egui_extras = { version = "0.33.0", features = ["all_loaders"] }
|
||||
#egui_plot = "0.29.0"
|
||||
image = { version = "0.25.6", default-features = false, features = ["png", "webp"] }
|
||||
image = { version = "0.25.8", default-features = false, features = ["png", "webp"] }
|
||||
|
||||
wide = "0.7.33"
|
||||
wide = "0.8.1"
|
||||
rayon = "1.11.0"
|
||||
ron = "0.11.0"
|
||||
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
|
||||
tracing = "0.1.41"
|
||||
tangled = { path = "tangled" }
|
||||
serde = { version = "1.0.219", features = ["serde_derive", "derive"] }
|
||||
tangled = "0.5.0"
|
||||
serde = { version = "1.0.228", features = ["serde_derive", "derive"] }
|
||||
bitcode = "0.6.7"
|
||||
lz4_flex = { version = "0.11.5", default-features = false, features = ["std"]}
|
||||
rand = "0.9.2"
|
||||
steamworks = "0.11.0"
|
||||
crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] }
|
||||
arboard = { version = "3.6.1", features = ["wayland-data-control"]}
|
||||
socket2 = { version = "0.6.0", features = ["all"] }
|
||||
reqwest = { version = "0.12.23", features = ["blocking", "json"]}
|
||||
socket2 = { version = "0.6.1", features = ["all"] }
|
||||
reqwest = { version = "0.12.24", features = ["blocking", "json"]}
|
||||
poll-promise = "0.3.0"
|
||||
zip = "4.5.0"
|
||||
zip = "6.0.0"
|
||||
self-replace = "1.5.0"
|
||||
rustc-hash = "2.1.1"
|
||||
fluent-templates = "0.13.1"
|
||||
fluent-templates = "0.13.2"
|
||||
unic-langid = { version = "0.9.6", features = ["serde"] }
|
||||
fluent-bundle = "0.16.0"
|
||||
argh = "0.1.13"
|
||||
|
|
@ -44,7 +40,7 @@ shlex = "1.3.0"
|
|||
quick-xml = { version = "0.38.3", features = ["serialize"] }
|
||||
dashmap = "6.1.0"
|
||||
eyre = "0.6.12"
|
||||
tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] }
|
||||
tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread"] }
|
||||
tracing-appender = "0.2.3"
|
||||
shared = {path = "../shared"}
|
||||
rstar = "0.12.2"
|
||||
|
|
@ -72,4 +68,4 @@ lto = true
|
|||
strip = true
|
||||
|
||||
[profile.release-lto]
|
||||
inherits = "release"
|
||||
inherits = "release"
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ use cpal::traits::{DeviceTrait, HostTrait};
|
|||
use eframe::egui::load::TexturePoll;
|
||||
use eframe::egui::{
|
||||
self, Align2, Button, Color32, ComboBox, Context, DragValue, FontDefinitions, FontFamily,
|
||||
ImageButton, InnerResponse, Key, Layout, Margin, OpenUrl, Rect, RichText, ScrollArea, Sense,
|
||||
SizeHint, Slider, TextureOptions, ThemePreference, Ui, UiBuilder, Vec2, Visuals, Window, pos2,
|
||||
InnerResponse, Key, Layout, Margin, OpenUrl, Rect, RichText, ScrollArea, Sense, SizeHint,
|
||||
Slider, TextureOptions, ThemePreference, Ui, UiBuilder, Vec2, Visuals, Window, pos2,
|
||||
};
|
||||
use eframe::epaint::TextureHandle;
|
||||
use image::DynamicImage::ImageRgba8;
|
||||
|
|
@ -1357,7 +1357,7 @@ fn square_button_icon(ui: &mut Ui, icon: egui::Image) -> egui::Response {
|
|||
let side = ui.available_width();
|
||||
ui.add_sized(
|
||||
[side, side],
|
||||
ImageButton::new(icon), // Somewhy it doesnt inherit style correctly
|
||||
Button::image(icon), // Somewhy it doesnt inherit style correctly
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1663,7 +1663,7 @@ impl App {
|
|||
if self.app_saved_state.times_started.is_multiple_of(20) {
|
||||
let image = egui::Image::new(egui::include_image!("../assets/longleg.png"))
|
||||
.texture_options(TextureOptions::NEAREST);
|
||||
image.paint_at(ui, ctx.screen_rect());
|
||||
image.paint_at(ui, ctx.viewport_rect());
|
||||
} else {
|
||||
draw_bg(ui);
|
||||
}
|
||||
|
|
@ -2480,7 +2480,7 @@ fn draw_bg(ui: &mut Ui) {
|
|||
let image = egui::Image::new(egui::include_image!("../assets/noita_ew_logo_sq.webp"))
|
||||
.texture_options(TextureOptions::NEAREST);
|
||||
|
||||
let rect = ui.ctx().screen_rect();
|
||||
let rect = ui.ctx().viewport_rect();
|
||||
let aspect_ratio = 1.0;
|
||||
let new_height = f32::max(rect.width() * aspect_ratio, rect.height());
|
||||
let new_width = new_height / aspect_ratio;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl OmniPeerId {
|
|||
pub enum OmniNetworkEvent {
|
||||
PeerConnected(OmniPeerId),
|
||||
PeerDisconnected(OmniPeerId),
|
||||
Message { src: OmniPeerId, data: Vec<u8> },
|
||||
Message { src: OmniPeerId, data: Box<[u8]> },
|
||||
}
|
||||
|
||||
impl From<tangled::NetworkEvent> for OmniNetworkEvent {
|
||||
|
|
@ -99,7 +99,7 @@ impl PeerVariant {
|
|||
reliability: Reliability,
|
||||
) -> Result<(), tangled::NetError> {
|
||||
match self {
|
||||
PeerVariant::Tangled(p) => p.send(peer.into(), msg, reliability),
|
||||
PeerVariant::Tangled(p) => p.send(peer.into(), &msg, reliability),
|
||||
PeerVariant::Steam(p) => {
|
||||
p.send_message(peer.into(), &msg, reliability)
|
||||
.map_err(|e| match e {
|
||||
|
|
@ -127,7 +127,7 @@ impl PeerVariant {
|
|||
reliability: Reliability,
|
||||
) -> Result<(), tangled::NetError> {
|
||||
match self {
|
||||
PeerVariant::Tangled(p) => p.broadcast(msg, reliability),
|
||||
PeerVariant::Tangled(p) => p.broadcast(&msg, reliability),
|
||||
PeerVariant::Steam(p) => {
|
||||
p.broadcast_message(&msg, reliability);
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ impl SteamPeer {
|
|||
.expect("only steam ids are supported");
|
||||
returned_events.push(OmniNetworkEvent::Message {
|
||||
src: steam_id.into(),
|
||||
data: message.data().to_vec(), // TODO eliminate clone here.
|
||||
data: message.data().into(),
|
||||
})
|
||||
}
|
||||
let mut fully_connected = self.connections.connected.lock().unwrap();
|
||||
|
|
|
|||
1
noita-proxy/tangled/.gitignore
vendored
1
noita-proxy/tangled/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/target
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
[package]
|
||||
name = "tangled"
|
||||
version = "0.4.0"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/IntQuant/tangled"
|
||||
categories = ["network-programming", ]
|
||||
description = "Work-in-progress UDP networking crate."
|
||||
|
||||
|
||||
[[example]]
|
||||
name = "chat"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
crossbeam = "0.8.4"
|
||||
tracing = "0.1.41"
|
||||
dashmap = "6.1.0"
|
||||
quinn = "0.11.9"
|
||||
rcgen = "0.14.3"
|
||||
thiserror = "2.0.16"
|
||||
tokio = { version = "1.47.1", features = ["macros", "io-util", "sync"] }
|
||||
bitcode = "0.6.7"
|
||||
socket2 = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
test-log = { version = "0.2.18", default-features = false, features = ["trace"]}
|
||||
tracing-subscriber = {version = "0.3", features = ["env-filter", "fmt"]}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 "IntQuant"
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Work-in-progress UDP networking crate.
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
use std::{
|
||||
env::args,
|
||||
io::stdin,
|
||||
thread::{sleep, spawn},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crossbeam::channel::bounded;
|
||||
use tangled::{Peer, Reliability};
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
fn main() {
|
||||
let subscriber = FmtSubscriber::builder()
|
||||
.with_max_level(Level::DEBUG)
|
||||
.finish();
|
||||
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||
|
||||
let mut args = args().skip(1);
|
||||
let peer = match args.next().as_deref() {
|
||||
Some("host") => {
|
||||
let bind_addr = match args.next().and_then(|arg| arg.parse().ok()) {
|
||||
Some(addr) => addr,
|
||||
None => {
|
||||
println!("Expected an address:port to host on as a second argument");
|
||||
return;
|
||||
}
|
||||
};
|
||||
Peer::host(bind_addr, None)
|
||||
}
|
||||
Some("connect") => {
|
||||
let connect_addr = match args.next().and_then(|arg| arg.parse().ok()) {
|
||||
Some(addr) => addr,
|
||||
None => {
|
||||
println!("Expected an address:port to connect to as a second argument");
|
||||
return;
|
||||
}
|
||||
};
|
||||
Peer::connect(connect_addr, None)
|
||||
}
|
||||
Some(_) | None => {
|
||||
println!("First argument should be one of 'host', 'connect'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
.unwrap();
|
||||
let (s, r) = bounded(1);
|
||||
spawn(move || {
|
||||
for msg in stdin().lines() {
|
||||
s.send(msg.unwrap()).unwrap();
|
||||
}
|
||||
});
|
||||
loop {
|
||||
for msg in peer.recv() {
|
||||
match msg {
|
||||
tangled::NetworkEvent::PeerConnected(id) => println!("Peer connected: {}", id),
|
||||
tangled::NetworkEvent::PeerDisconnected(id) => {
|
||||
println!("Peer disconnected: {}", id)
|
||||
}
|
||||
tangled::NetworkEvent::Message(msg) => {
|
||||
println!("{}", String::from_utf8_lossy(&msg.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
for msg in r.try_iter() {
|
||||
println!("State: {:?}", peer.state());
|
||||
let data = msg.as_bytes();
|
||||
for destination in peer.iter_peer_ids() {
|
||||
if destination == peer.my_id().unwrap() {
|
||||
continue;
|
||||
}
|
||||
println!("Sent to {}", destination);
|
||||
peer.send(destination, data.to_vec(), Reliability::Reliable)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
//! Various common public types.
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use bitcode::{Decode, Encode};
|
||||
|
||||
/// Per-peer settings. Peers that are connected to the same host, as well as the host itself, should have the same settings.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Settings {}
|
||||
|
||||
/// Tells how reliable a message is.
|
||||
#[derive(Encode, Decode, Clone, Copy, PartialEq, Debug)]
|
||||
pub enum Reliability {
|
||||
/// Message will be delivered at most once.
|
||||
Unreliable,
|
||||
/// Message will be resent untill is's arrival will be confirmed.
|
||||
/// Will be delivered at most once.
|
||||
Reliable,
|
||||
}
|
||||
|
||||
impl Reliability {
|
||||
pub fn from_reliability_bool(reliable: bool) -> Reliability {
|
||||
if reliable {
|
||||
Reliability::Reliable
|
||||
} else {
|
||||
Reliability::Unreliable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Encode, Decode, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Destination {
|
||||
One(PeerId),
|
||||
Broadcast,
|
||||
}
|
||||
|
||||
/// A value which refers to a specific peer.
|
||||
/// Peer 0 is always the host.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Encode, Decode)]
|
||||
pub struct PeerId(pub u16);
|
||||
|
||||
/// Possible network events, returned by `Peer.recv()`.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum NetworkEvent {
|
||||
/// A new peer has connected.
|
||||
PeerConnected(PeerId),
|
||||
/// Peer has disconnected.
|
||||
PeerDisconnected(PeerId),
|
||||
/// Message has been received.
|
||||
Message(Message),
|
||||
}
|
||||
|
||||
/// A message received from a peer.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Message {
|
||||
/// Original peer who sent the message.
|
||||
pub src: PeerId,
|
||||
/// The data that has been sent.
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Current peer state
|
||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum PeerState {
|
||||
/// Waiting for connection. Switches to `Connected` right after id from the host has been acquired.
|
||||
/// Note: hosts switches to 'Connected' basically instantly.
|
||||
#[default]
|
||||
PendingConnection,
|
||||
/// Connected to host and ready to send/receive messages.
|
||||
Connected,
|
||||
/// No longer connected, won't reconnect.
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
impl Display for PeerState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PeerState::PendingConnection => write!(f, "Connection pending..."),
|
||||
PeerState::Connected => write!(f, "Connected"),
|
||||
PeerState::Disconnected => write!(f, "Disconnected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PeerId {
|
||||
pub const HOST: PeerId = PeerId(0);
|
||||
}
|
||||
|
||||
impl Display for PeerId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Destination {
|
||||
pub(crate) fn is_broadcast(self) -> bool {
|
||||
matches!(self, Destination::Broadcast)
|
||||
}
|
||||
pub(crate) fn to_one(self) -> Option<PeerId> {
|
||||
if let Self::One(peer_id) = self {
|
||||
Some(peer_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,525 +0,0 @@
|
|||
use std::{
|
||||
io,
|
||||
net::SocketAddr,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use bitcode::{Decode, Encode};
|
||||
use crossbeam::{
|
||||
atomic::AtomicCell,
|
||||
channel::{Receiver, Sender, unbounded},
|
||||
};
|
||||
use dashmap::DashMap;
|
||||
use quinn::{
|
||||
ClientConfig, ConnectError, Connecting, ConnectionError, Endpoint, Incoming, RecvStream,
|
||||
ServerConfig, TransportConfig,
|
||||
crypto::rustls::QuicClientConfig,
|
||||
rustls::{
|
||||
self,
|
||||
pki_types::{CertificateDer, PrivatePkcs8KeyDer},
|
||||
},
|
||||
};
|
||||
use socket2::{Domain, Socket, Type};
|
||||
use thiserror::Error;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use crate::{
|
||||
common::{Destination, NetworkEvent, PeerId, PeerState, Reliability, Settings},
|
||||
helpers::SkipServerVerification,
|
||||
};
|
||||
|
||||
mod message_stream;
|
||||
|
||||
#[derive(Debug, Encode, Decode)]
|
||||
enum InternalMessage {
|
||||
Normal(OutboundMessage),
|
||||
RemoteConnected(PeerId),
|
||||
RemoteDisconnected(PeerId),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct RemotePeer;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum DirectConnectionError {
|
||||
#[error("QUIC Connection error: {0}")]
|
||||
QUICConnectionError(#[from] ConnectionError),
|
||||
#[error("Initial exchange failed")]
|
||||
InitialExchangeFailed,
|
||||
#[error("Message read failed")]
|
||||
MessageIoFailed,
|
||||
#[error("Failed to decode message")]
|
||||
DecodeError,
|
||||
}
|
||||
|
||||
struct DirectPeer {
|
||||
my_id: PeerId,
|
||||
remote_id: PeerId,
|
||||
send_stream: message_stream::SendMessageStream<InternalMessage>,
|
||||
}
|
||||
|
||||
impl DirectPeer {
|
||||
async fn recv_task(shared: Arc<Shared>, recv_stream: RecvStream, remote_id: PeerId) {
|
||||
let mut recv_stream = message_stream::RecvMessageStream::new(recv_stream);
|
||||
while let Ok(msg) = recv_stream.recv().await {
|
||||
trace!("Received message from {remote_id}");
|
||||
if let Err(err) = shared
|
||||
.internal_incoming_messages_s
|
||||
.send((remote_id, msg))
|
||||
.await
|
||||
{
|
||||
warn!("Could not send message to channel: {err}. Stopping.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
shared
|
||||
.internal_events_s
|
||||
.send(InternalEvent::Disconnected(remote_id))
|
||||
.ok();
|
||||
}
|
||||
|
||||
async fn accept(
|
||||
shared: Arc<Shared>,
|
||||
incoming: Incoming,
|
||||
assigned_peer_id: PeerId,
|
||||
) -> Result<Self, DirectConnectionError> {
|
||||
let connection = incoming
|
||||
.await
|
||||
.inspect_err(|err| warn!("Failed to accept connection: {err}"))?;
|
||||
|
||||
let mut sender = connection
|
||||
.open_uni()
|
||||
.await
|
||||
.inspect_err(|err| warn!("Failed to get send stream: {err}"))?;
|
||||
sender
|
||||
.write_u16(assigned_peer_id.0)
|
||||
.await
|
||||
.map_err(|_err| DirectConnectionError::InitialExchangeFailed)?;
|
||||
|
||||
let (send_stream, recv_stream) = connection.open_bi().await?;
|
||||
tokio::spawn(Self::recv_task(shared, recv_stream, assigned_peer_id));
|
||||
debug!("Server: spawned recv task");
|
||||
|
||||
Ok(Self {
|
||||
my_id: PeerId::HOST,
|
||||
remote_id: assigned_peer_id,
|
||||
send_stream: message_stream::SendMessageStream::new(send_stream),
|
||||
})
|
||||
}
|
||||
|
||||
async fn connect(
|
||||
shared: Arc<Shared>,
|
||||
connection: Connecting,
|
||||
) -> Result<Self, DirectConnectionError> {
|
||||
let connection = connection
|
||||
.await
|
||||
.inspect_err(|err| warn!("Failed to initiate connection: {err}"))?;
|
||||
|
||||
let mut receiver = connection.accept_uni().await?;
|
||||
let peer_id = receiver
|
||||
.read_u16()
|
||||
.await
|
||||
.map_err(|_err| DirectConnectionError::InitialExchangeFailed)?;
|
||||
debug!("Got peer id {peer_id}");
|
||||
|
||||
let (send_stream, recv_stream) = connection.accept_bi().await?;
|
||||
tokio::spawn(Self::recv_task(shared, recv_stream, PeerId::HOST));
|
||||
debug!("Client: spawned recv task");
|
||||
|
||||
Ok(Self {
|
||||
my_id: PeerId(peer_id),
|
||||
remote_id: PeerId::HOST,
|
||||
send_stream: message_stream::SendMessageStream::new(send_stream),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Encode, Decode, Clone)]
|
||||
pub(crate) struct OutboundMessage {
|
||||
pub src: PeerId,
|
||||
pub dst: Destination,
|
||||
pub reliability: Reliability,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub(crate) type Channel<T> = (Sender<T>, Receiver<T>);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TangledInitError {
|
||||
#[error("Could not create endpoint.\nReason: {0}")]
|
||||
CouldNotCreateEndpoint(io::Error),
|
||||
#[error("Could not connect to host.\nReason: {0}")]
|
||||
CouldNotConnectToHost(ConnectError),
|
||||
#[error("Async runtime not found")]
|
||||
NoRuntimeFound,
|
||||
}
|
||||
|
||||
enum InternalEvent {
|
||||
Connected(PeerId),
|
||||
Disconnected(PeerId),
|
||||
}
|
||||
|
||||
pub(crate) struct Shared {
|
||||
pub inbound_channel: Channel<NetworkEvent>,
|
||||
pub outbound_messages_s: tokio::sync::mpsc::UnboundedSender<OutboundMessage>,
|
||||
pub keep_alive: AtomicBool,
|
||||
pub peer_state: AtomicCell<PeerState>,
|
||||
pub remote_peers: DashMap<PeerId, RemotePeer>,
|
||||
pub host_addr: Option<SocketAddr>,
|
||||
pub my_id: AtomicCell<Option<PeerId>>,
|
||||
// ConnectionManager-specific stuff
|
||||
direct_peers: DashMap<PeerId, DirectPeer>,
|
||||
internal_incoming_messages_s: tokio::sync::mpsc::Sender<(PeerId, InternalMessage)>,
|
||||
internal_events_s: tokio::sync::mpsc::UnboundedSender<InternalEvent>,
|
||||
}
|
||||
|
||||
pub(crate) struct ConnectionManager {
|
||||
shared: Arc<Shared>,
|
||||
endpoint: Endpoint,
|
||||
host_conn: Option<DirectPeer>,
|
||||
is_server: bool,
|
||||
incoming_messages_r: tokio::sync::mpsc::Receiver<(PeerId, InternalMessage)>,
|
||||
outbound_messages_r: tokio::sync::mpsc::UnboundedReceiver<OutboundMessage>,
|
||||
internal_events_r: tokio::sync::mpsc::UnboundedReceiver<InternalEvent>,
|
||||
}
|
||||
|
||||
impl ConnectionManager {
|
||||
pub(crate) fn new(
|
||||
host_addr: Option<SocketAddr>,
|
||||
_settings: Option<Settings>,
|
||||
bind_addr: SocketAddr,
|
||||
) -> Result<Self, TangledInitError> {
|
||||
let is_server = host_addr.is_none();
|
||||
|
||||
let (internal_incoming_messages_s, incoming_messages_r) = tokio::sync::mpsc::channel(512);
|
||||
let (outbound_messages_s, outbound_messages_r) = tokio::sync::mpsc::unbounded_channel();
|
||||
let (internal_events_s, internal_events_r) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
let shared = Arc::new(Shared {
|
||||
inbound_channel: unbounded(),
|
||||
outbound_messages_s,
|
||||
keep_alive: AtomicBool::new(true),
|
||||
host_addr,
|
||||
peer_state: Default::default(),
|
||||
remote_peers: Default::default(),
|
||||
my_id: AtomicCell::new(is_server.then_some(PeerId(0))),
|
||||
direct_peers: DashMap::default(),
|
||||
internal_incoming_messages_s,
|
||||
internal_events_s,
|
||||
});
|
||||
|
||||
let server_config = default_server_config();
|
||||
|
||||
let mut endpoint = if is_server {
|
||||
// Endpoint::server(config, bind_addr).map_err(TangledInitError::CouldNotCreateEndpoint)?
|
||||
let socket = Socket::new(Domain::for_address(bind_addr), Type::DGRAM, None)
|
||||
.map_err(TangledInitError::CouldNotCreateEndpoint)?;
|
||||
if bind_addr.is_ipv6() {
|
||||
if let Err(err) = socket.set_only_v6(false) {
|
||||
warn!("Failed to set socket to be not only v6: {}", err);
|
||||
} else {
|
||||
info!("Enabled dualstack mode for socket");
|
||||
};
|
||||
}
|
||||
socket
|
||||
.bind(&bind_addr.into())
|
||||
.map_err(TangledInitError::CouldNotCreateEndpoint)?;
|
||||
|
||||
let runtime = quinn::default_runtime().ok_or(TangledInitError::NoRuntimeFound)?;
|
||||
Endpoint::new_with_abstract_socket(
|
||||
Default::default(),
|
||||
Some(server_config),
|
||||
runtime
|
||||
.wrap_udp_socket(socket.into())
|
||||
.map_err(TangledInitError::CouldNotCreateEndpoint)?,
|
||||
runtime,
|
||||
)
|
||||
.map_err(TangledInitError::CouldNotCreateEndpoint)?
|
||||
} else {
|
||||
Endpoint::client(bind_addr).map_err(TangledInitError::CouldNotCreateEndpoint)?
|
||||
};
|
||||
|
||||
endpoint.set_default_client_config(ClientConfig::new(Arc::new(
|
||||
QuicClientConfig::try_from(
|
||||
rustls::ClientConfig::builder()
|
||||
.dangerous()
|
||||
.with_custom_certificate_verifier(SkipServerVerification::new())
|
||||
.with_no_client_auth(),
|
||||
)
|
||||
.unwrap(),
|
||||
)));
|
||||
|
||||
Ok(Self {
|
||||
shared,
|
||||
is_server,
|
||||
endpoint,
|
||||
host_conn: None,
|
||||
incoming_messages_r,
|
||||
outbound_messages_r,
|
||||
internal_events_r,
|
||||
})
|
||||
}
|
||||
|
||||
async fn accept_connections(shared: Arc<Shared>, endpoint: Endpoint) {
|
||||
let mut peer_id_counter = 1;
|
||||
while shared.keep_alive.load(Ordering::Relaxed) {
|
||||
let Some(incoming) = endpoint.accept().await else {
|
||||
debug!("Endpoint closed, stopping connection accepter task.");
|
||||
return;
|
||||
};
|
||||
match DirectPeer::accept(shared.clone(), incoming, PeerId(peer_id_counter)).await {
|
||||
Ok(direct_peer) => {
|
||||
shared
|
||||
.direct_peers
|
||||
.insert(PeerId(peer_id_counter), direct_peer);
|
||||
shared
|
||||
.internal_events_s
|
||||
.send(InternalEvent::Connected(PeerId(peer_id_counter)))
|
||||
.expect("channel to be open");
|
||||
peer_id_counter += 1;
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Failed to accept connection: {err}")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_incoming_message(&mut self, msg: InternalMessage) {
|
||||
match msg {
|
||||
InternalMessage::Normal(msg) => {
|
||||
let intended_for_me = self
|
||||
.shared
|
||||
.my_id
|
||||
.load()
|
||||
.map(|my_id| msg.dst == Destination::One(my_id))
|
||||
.unwrap_or(false);
|
||||
if self.is_server && !intended_for_me && !msg.dst.is_broadcast() {
|
||||
self.server_send_internal_message(
|
||||
msg.dst.to_one().unwrap(),
|
||||
&InternalMessage::Normal(msg),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
if self.is_server && msg.dst.is_broadcast() {
|
||||
self.server_send_to_peers(msg.clone()).await;
|
||||
}
|
||||
if msg.dst.is_broadcast() || intended_for_me {
|
||||
self.shared
|
||||
.inbound_channel
|
||||
.0
|
||||
.send(NetworkEvent::Message(crate::Message {
|
||||
src: msg.src,
|
||||
data: msg.data,
|
||||
}))
|
||||
.expect("channel to be open");
|
||||
}
|
||||
}
|
||||
InternalMessage::RemoteConnected(peer_id) => {
|
||||
debug!("Got notified of peer {peer_id}");
|
||||
self.shared
|
||||
.internal_events_s
|
||||
.send(InternalEvent::Connected(peer_id))
|
||||
.expect("channel to be open");
|
||||
}
|
||||
InternalMessage::RemoteDisconnected(peer_id) => self
|
||||
.shared
|
||||
.internal_events_s
|
||||
.send(InternalEvent::Disconnected(peer_id))
|
||||
.expect("channel to be open"),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_internal_event(&mut self, ev: InternalEvent) {
|
||||
match ev {
|
||||
InternalEvent::Connected(peer_id) => {
|
||||
if self.shared.remote_peers.contains_key(&peer_id) {
|
||||
// Already connected, no need to emit an event.
|
||||
return;
|
||||
}
|
||||
self.shared
|
||||
.inbound_channel
|
||||
.0
|
||||
.send(NetworkEvent::PeerConnected(peer_id))
|
||||
.expect("channel to be open");
|
||||
self.shared.remote_peers.insert(peer_id, RemotePeer);
|
||||
debug!(
|
||||
"Peer {} connected, total connected: {}",
|
||||
peer_id,
|
||||
self.shared.remote_peers.len()
|
||||
);
|
||||
if self.is_server {
|
||||
self.server_broadcast_internal_message(
|
||||
PeerId::HOST,
|
||||
InternalMessage::RemoteConnected(peer_id),
|
||||
)
|
||||
.await;
|
||||
|
||||
let peers = self
|
||||
.shared
|
||||
.remote_peers
|
||||
.iter()
|
||||
.map(|i| *i.key())
|
||||
.collect::<Vec<_>>();
|
||||
for conn_peer in peers {
|
||||
debug!("Notifying peer of {conn_peer}");
|
||||
self.server_send_internal_message(
|
||||
peer_id,
|
||||
&InternalMessage::RemoteConnected(conn_peer),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
InternalEvent::Disconnected(peer_id) => {
|
||||
debug!("Peer {} disconnected", peer_id);
|
||||
self.shared.direct_peers.remove(&peer_id);
|
||||
self.shared
|
||||
.inbound_channel
|
||||
.0
|
||||
.send(NetworkEvent::PeerDisconnected(peer_id))
|
||||
.expect("channel to be open");
|
||||
self.shared.remote_peers.remove(&peer_id);
|
||||
if self.is_server {
|
||||
self.server_broadcast_internal_message(
|
||||
PeerId::HOST,
|
||||
InternalMessage::RemoteDisconnected(peer_id),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn server_send_to_peers(&mut self, msg: OutboundMessage) {
|
||||
match msg.dst {
|
||||
Destination::One(peer_id) => {
|
||||
self.server_send_internal_message(peer_id, &InternalMessage::Normal(msg))
|
||||
.await;
|
||||
}
|
||||
Destination::Broadcast => {
|
||||
let msg_src = msg.src;
|
||||
let value = InternalMessage::Normal(msg);
|
||||
self.server_broadcast_internal_message(msg_src, value).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn server_send_internal_message(&mut self, peer_id: PeerId, msg: &InternalMessage) {
|
||||
let peer = self.shared.direct_peers.get_mut(&peer_id);
|
||||
// TODO handle lack of peer?
|
||||
if let Some(mut peer) = peer {
|
||||
// TODO handle errors
|
||||
peer.send_stream.send(msg).await.ok();
|
||||
}
|
||||
}
|
||||
|
||||
async fn server_broadcast_internal_message(
|
||||
&mut self,
|
||||
excluded: PeerId,
|
||||
value: InternalMessage,
|
||||
) {
|
||||
for mut peer in self.shared.direct_peers.iter_mut() {
|
||||
let peer_id = *peer.key();
|
||||
if peer_id != excluded {
|
||||
// TODO handle errors
|
||||
peer.send_stream.send(&value).await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn astart(mut self, host_conn: Option<Connecting>) {
|
||||
debug!("astart running");
|
||||
if let Some(host_conn) = host_conn {
|
||||
match DirectPeer::connect(self.shared.clone(), host_conn).await {
|
||||
Ok(host_conn) => {
|
||||
self.shared.my_id.store(Some(host_conn.my_id));
|
||||
self.shared
|
||||
.internal_events_s
|
||||
.send(InternalEvent::Connected(host_conn.remote_id))
|
||||
.expect("channel to be open");
|
||||
self.host_conn = Some(host_conn);
|
||||
self.shared.peer_state.store(PeerState::Connected);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Could not connect to host: {}", err);
|
||||
self.shared.peer_state.store(PeerState::Disconnected);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.is_server {
|
||||
let endpoint = self.endpoint.clone();
|
||||
tokio::spawn(Self::accept_connections(self.shared.clone(), endpoint));
|
||||
debug!("Started connection acceptor task");
|
||||
}
|
||||
|
||||
while self.shared.keep_alive.load(Ordering::Relaxed) {
|
||||
tokio::select! {
|
||||
msg = self.incoming_messages_r.recv() => {
|
||||
let msg = msg.expect("channel to not be closed");
|
||||
self.handle_incoming_message(msg.1).await;
|
||||
}
|
||||
msg = self.outbound_messages_r.recv() => {
|
||||
let msg = msg.expect("channel to not be closed");
|
||||
if self.is_server {
|
||||
self.server_send_to_peers(msg).await;
|
||||
} else {
|
||||
// TODO handle error
|
||||
self.host_conn.as_mut().unwrap().send_stream.send(&InternalMessage::Normal(msg)).await.ok();
|
||||
}
|
||||
}
|
||||
ev = self.internal_events_r.recv() => {
|
||||
let ev = ev.expect("channel to not be closed");
|
||||
self.handle_internal_event(ev).await;
|
||||
}
|
||||
// Check if we need to stop periodically.
|
||||
_ = tokio::time::sleep(Duration::from_millis(1000)) => {}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Closing endpoint");
|
||||
self.endpoint
|
||||
.close(0u32.into(), b"peer decided to disconnect");
|
||||
}
|
||||
|
||||
pub(crate) fn start(self) -> Result<(), TangledInitError> {
|
||||
let host_conn = self
|
||||
.shared
|
||||
.host_addr
|
||||
.as_ref()
|
||||
.map(|host_addr| {
|
||||
self.endpoint
|
||||
.connect(*host_addr, "tangled")
|
||||
.map_err(TangledInitError::CouldNotConnectToHost)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
debug!("Spawning astart task");
|
||||
tokio::spawn(self.astart(host_conn));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn shared(&self) -> Arc<Shared> {
|
||||
self.shared.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn default_server_config() -> ServerConfig {
|
||||
let cert = rcgen::generate_simple_self_signed(vec!["tangled".into()]).unwrap();
|
||||
let cert_der = CertificateDer::from(cert.cert);
|
||||
let priv_key = PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der());
|
||||
|
||||
let mut config =
|
||||
ServerConfig::with_single_cert(vec![cert_der.clone()], priv_key.into()).unwrap();
|
||||
let mut transport_config = TransportConfig::default();
|
||||
transport_config.keep_alive_interval(Some(Duration::from_secs(10)));
|
||||
config.transport_config(Arc::new(transport_config));
|
||||
config
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use bitcode::{DecodeOwned, Encode};
|
||||
use quinn::{RecvStream, SendStream};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tracing::trace;
|
||||
|
||||
use super::DirectConnectionError;
|
||||
|
||||
pub(crate) struct SendMessageStream<Msg> {
|
||||
inner: SendStream,
|
||||
_phantom: PhantomData<fn(Msg)>,
|
||||
}
|
||||
|
||||
pub(crate) struct RecvMessageStream<Msg> {
|
||||
inner: RecvStream,
|
||||
_phantom: PhantomData<fn() -> Msg>,
|
||||
}
|
||||
|
||||
impl<Msg: Encode> SendMessageStream<Msg> {
|
||||
pub(crate) fn new(inner: SendStream) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_raw(&mut self, msg: &[u8]) -> Result<(), DirectConnectionError> {
|
||||
self.inner
|
||||
.write_u32(
|
||||
msg.len()
|
||||
.try_into()
|
||||
.expect("Only messages up to ~4GB supported"),
|
||||
)
|
||||
.await
|
||||
.map_err(|_err| DirectConnectionError::MessageIoFailed)?;
|
||||
self.inner
|
||||
.write_all(msg)
|
||||
.await
|
||||
.map_err(|_err| DirectConnectionError::MessageIoFailed)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn send(&mut self, msg: &Msg) -> Result<(), DirectConnectionError> {
|
||||
let msg = bitcode::encode(msg);
|
||||
self.send_raw(&msg).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<Msg: DecodeOwned> RecvMessageStream<Msg> {
|
||||
pub(crate) fn new(inner: RecvStream) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
async fn recv_raw(&mut self) -> Result<Vec<u8>, DirectConnectionError> {
|
||||
let len = self
|
||||
.inner
|
||||
.read_u32()
|
||||
.await
|
||||
.map_err(|_err| DirectConnectionError::MessageIoFailed)?;
|
||||
trace!("Expecting message of len {len}");
|
||||
let mut buf = vec![0; len as usize];
|
||||
self.inner
|
||||
.read_exact(&mut buf)
|
||||
.await
|
||||
.map_err(|_err| DirectConnectionError::MessageIoFailed)?;
|
||||
Ok(buf)
|
||||
}
|
||||
pub(crate) async fn recv(&mut self) -> Result<Msg, DirectConnectionError> {
|
||||
let raw = self.recv_raw().await?;
|
||||
bitcode::decode(&raw).map_err(|_| DirectConnectionError::DecodeError)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
use std::{error::Error, fmt::Display};
|
||||
|
||||
use crossbeam::channel::SendError;
|
||||
|
||||
use crate::MAX_MESSAGE_LEN;
|
||||
|
||||
/// Describes possible errors
|
||||
#[derive(Debug)]
|
||||
pub enum NetError {
|
||||
/// Tried to use an invalid peer id.
|
||||
UnknownPeer,
|
||||
/// Peer is not able to communicate with other peers anymore.
|
||||
Disconnected,
|
||||
/// Tried to send a message longer than `MAX_MESSAGE_LEN`.
|
||||
MessageTooLong,
|
||||
/// Unreliable message was instantly dropped because there are too many packets waiting to be sent.
|
||||
Dropped,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl Display for NetError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
NetError::UnknownPeer => write!(f, "No peer with this id"),
|
||||
NetError::Disconnected => write!(f, "Not connected"),
|
||||
NetError::MessageTooLong => {
|
||||
write!(f, "Message len exceeds the limit of {MAX_MESSAGE_LEN}")
|
||||
}
|
||||
NetError::Dropped => write!(f, "Message dropped"),
|
||||
NetError::Other => write!(f, "Other"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for NetError {}
|
||||
|
||||
impl<T> From<SendError<T>> for NetError {
|
||||
fn from(_: SendError<T>) -> Self {
|
||||
Self::Disconnected
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use quinn::rustls::{
|
||||
self,
|
||||
pki_types::{CertificateDer, ServerName, UnixTime},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);
|
||||
|
||||
impl SkipServerVerification {
|
||||
pub(crate) fn new() -> Arc<Self> {
|
||||
Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider())))
|
||||
}
|
||||
}
|
||||
|
||||
impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_end_entity: &CertificateDer<'_>,
|
||||
_intermediates: &[CertificateDer<'_>],
|
||||
_server_name: &ServerName<'_>,
|
||||
_ocsp: &[u8],
|
||||
_now: UnixTime,
|
||||
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
|
||||
Ok(rustls::client::danger::ServerCertVerified::assertion())
|
||||
}
|
||||
|
||||
fn verify_tls12_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
cert: &CertificateDer<'_>,
|
||||
dss: &rustls::DigitallySignedStruct,
|
||||
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||
rustls::crypto::verify_tls12_signature(
|
||||
message,
|
||||
cert,
|
||||
dss,
|
||||
&self.0.signature_verification_algorithms,
|
||||
)
|
||||
}
|
||||
|
||||
fn verify_tls13_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
cert: &CertificateDer<'_>,
|
||||
dss: &rustls::DigitallySignedStruct,
|
||||
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||
rustls::crypto::verify_tls13_signature(
|
||||
message,
|
||||
cert,
|
||||
dss,
|
||||
&self.0.signature_verification_algorithms,
|
||||
)
|
||||
}
|
||||
|
||||
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
|
||||
self.0.signature_verification_algorithms.supported_schemes()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,337 +0,0 @@
|
|||
//! Tangled - a work-in-progress UDP networking crate.
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use connection_manager::{
|
||||
ConnectionManager, OutboundMessage, RemotePeer, Shared, TangledInitError,
|
||||
};
|
||||
|
||||
pub use error::NetError;
|
||||
|
||||
/// Maximum size of a message which fits into a single datagram.
|
||||
/// Somewhat arbitrary, but if it gets this large something probably went wrong.
|
||||
pub const MAX_MESSAGE_LEN: usize = 2 * 1024 * 1024 * 1024;
|
||||
|
||||
mod common;
|
||||
mod connection_manager;
|
||||
mod error;
|
||||
mod helpers;
|
||||
|
||||
pub use common::*;
|
||||
use tracing::debug;
|
||||
|
||||
/// Represents a network endpoint. Can be constructed in either `host` or `client` mode.
|
||||
///
|
||||
/// Client can only connect to hosts, but they are able to send messages to any other peer connected to the same host, including the host itself.
|
||||
#[derive(Clone)]
|
||||
pub struct Peer {
|
||||
shared: Arc<Shared>,
|
||||
}
|
||||
|
||||
impl Peer {
|
||||
fn new(
|
||||
bind_addr: SocketAddr,
|
||||
host_addr: Option<SocketAddr>,
|
||||
settings: Option<Settings>,
|
||||
) -> Result<Self, TangledInitError> {
|
||||
let connection_manager = ConnectionManager::new(host_addr, settings, bind_addr)?;
|
||||
let shared = connection_manager.shared();
|
||||
if host_addr.is_none() {
|
||||
shared.remote_peers.insert(PeerId(0), RemotePeer);
|
||||
shared
|
||||
.inbound_channel
|
||||
.0
|
||||
.send(NetworkEvent::PeerConnected(PeerId(0)))
|
||||
.unwrap();
|
||||
}
|
||||
debug!("Starting connection manager");
|
||||
connection_manager.start()?;
|
||||
Ok(Peer { shared })
|
||||
}
|
||||
|
||||
pub fn remove(&self, peer: PeerId) {
|
||||
self.shared.remote_peers.remove(&peer);
|
||||
}
|
||||
|
||||
/// Host at a specified `bind_addr`.
|
||||
pub fn host(
|
||||
bind_addr: SocketAddr,
|
||||
settings: Option<Settings>,
|
||||
) -> Result<Self, TangledInitError> {
|
||||
Self::new(bind_addr, None, settings)
|
||||
}
|
||||
|
||||
/// Connect to a specified `host_addr`.
|
||||
pub fn connect(
|
||||
host_addr: SocketAddr,
|
||||
settings: Option<Settings>,
|
||||
) -> Result<Self, TangledInitError> {
|
||||
Self::new("[::]:0".parse().unwrap(), Some(host_addr), settings)
|
||||
}
|
||||
|
||||
/// Send a message to a specified single peer.
|
||||
pub fn send(
|
||||
&self,
|
||||
destination: PeerId,
|
||||
data: Vec<u8>,
|
||||
reliability: Reliability,
|
||||
) -> Result<(), NetError> {
|
||||
self.send_internal(Destination::One(destination), data, reliability)
|
||||
}
|
||||
|
||||
pub fn broadcast(&self, data: Vec<u8>, reliability: Reliability) -> Result<(), NetError> {
|
||||
self.send_internal(Destination::Broadcast, data, reliability)
|
||||
}
|
||||
|
||||
fn send_internal(
|
||||
&self,
|
||||
destination: Destination,
|
||||
data: Vec<u8>,
|
||||
reliability: Reliability,
|
||||
) -> Result<(), NetError> {
|
||||
self.shared
|
||||
.outbound_messages_s
|
||||
.send(OutboundMessage {
|
||||
src: self.my_id().expect("expected to know my_id by this point"),
|
||||
dst: destination,
|
||||
data,
|
||||
reliability,
|
||||
})
|
||||
.expect("channel to be open");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return an iterator over recieved messages.
|
||||
/// Does not block.
|
||||
pub fn recv(&self) -> impl Iterator<Item = NetworkEvent> + '_ {
|
||||
self.shared.inbound_channel.1.try_iter()
|
||||
}
|
||||
|
||||
/// Return an iterator over recieved messages.
|
||||
/// Blocking.
|
||||
pub fn recv_blocking(&self) -> impl Iterator<Item = NetworkEvent> + '_ {
|
||||
self.shared.inbound_channel.1.iter()
|
||||
}
|
||||
|
||||
/// Returns own `PeerId`, which can be used by any remote peer to send a message to this one.
|
||||
/// None is returned when not connected yet.
|
||||
pub fn my_id(&self) -> Option<PeerId> {
|
||||
self.shared.my_id.load()
|
||||
}
|
||||
|
||||
/// Current state of the peer.
|
||||
pub fn state(&self) -> PeerState {
|
||||
self.shared.peer_state.load()
|
||||
}
|
||||
|
||||
/// Iterate over connected peers, returning ther `PeerId`.
|
||||
pub fn iter_peer_ids(&self) -> impl Iterator<Item = PeerId> + '_ {
|
||||
self.shared
|
||||
.remote_peers
|
||||
.iter()
|
||||
.map(|item| item.key().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Peer {
|
||||
fn drop(&mut self) {
|
||||
self.shared
|
||||
.keep_alive
|
||||
.store(false, std::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::time::Duration;
|
||||
|
||||
use tracing::info;
|
||||
|
||||
use crate::{NetworkEvent, Peer, PeerId, Reliability, Settings, common::Message};
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_create_host() {
|
||||
let addr = "127.0.0.1:55999".parse().unwrap();
|
||||
let _host = Peer::host(addr, None).unwrap();
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_peer() {
|
||||
info!("Starting test_peer");
|
||||
let settings: Option<Settings> = Some(Default::default());
|
||||
let addr = "127.0.0.1:56001".parse().unwrap();
|
||||
let host = Peer::host(addr, settings.clone()).unwrap();
|
||||
assert_eq!(host.shared.remote_peers.len(), 1);
|
||||
let peer = Peer::connect(addr, settings.clone()).unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
assert_eq!(peer.shared.remote_peers.len(), 2);
|
||||
let data = vec![128, 51, 32];
|
||||
peer.send(PeerId(0), data.clone(), Reliability::Reliable)
|
||||
.unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
let host_events: Vec<_> = host.recv().collect();
|
||||
assert!(host_events.contains(&NetworkEvent::PeerConnected(PeerId(1))));
|
||||
assert!(host_events.contains(&NetworkEvent::Message(Message {
|
||||
data,
|
||||
src: PeerId(1)
|
||||
})));
|
||||
let peer_events: Vec<_> = peer.recv().collect();
|
||||
assert!(peer_events.contains(&NetworkEvent::PeerConnected(PeerId(0))));
|
||||
assert!(peer_events.contains(&NetworkEvent::PeerConnected(PeerId(1))));
|
||||
drop(peer);
|
||||
tokio::time::sleep(Duration::from_millis(1200)).await;
|
||||
assert_eq!(
|
||||
host.recv().next(),
|
||||
Some(NetworkEvent::PeerDisconnected(PeerId(1)))
|
||||
);
|
||||
assert_eq!(host.shared.remote_peers.len(), 1);
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_broadcast() {
|
||||
let settings: Option<Settings> = Some(Default::default());
|
||||
let addr = "127.0.0.1:56002".parse().unwrap();
|
||||
let host = Peer::host(addr, settings.clone()).unwrap();
|
||||
assert_eq!(host.shared.remote_peers.len(), 1);
|
||||
let peer1 = Peer::connect(addr, settings.clone()).unwrap();
|
||||
let peer2 = Peer::connect(addr, settings.clone()).unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
assert_eq!(host.shared.remote_peers.len(), 3);
|
||||
|
||||
let data = vec![123, 112, 51, 23];
|
||||
peer1
|
||||
.broadcast(data.clone(), Reliability::Reliable)
|
||||
.unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
|
||||
let host_events: Vec<_> = dbg!(host.recv().collect());
|
||||
let peer1_events: Vec<_> = dbg!(peer1.recv().collect());
|
||||
let peer2_events: Vec<_> = dbg!(peer2.recv().collect());
|
||||
|
||||
assert!(peer2_events.contains(&NetworkEvent::Message(Message {
|
||||
src: peer1.my_id().unwrap(),
|
||||
data: data.clone(),
|
||||
})));
|
||||
assert!(!peer1_events.contains(&NetworkEvent::Message(Message {
|
||||
src: peer1.my_id().unwrap(),
|
||||
data: data.clone(),
|
||||
})));
|
||||
assert!(host_events.contains(&NetworkEvent::Message(Message {
|
||||
src: peer1.my_id().unwrap(),
|
||||
data: data.clone(),
|
||||
})));
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_host_has_conn() {
|
||||
let settings: Option<Settings> = Some(Default::default());
|
||||
let addr = "127.0.0.1:56003".parse().unwrap();
|
||||
let host = Peer::host(addr, settings.clone()).unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
assert_eq!(
|
||||
host.recv().next(),
|
||||
Some(NetworkEvent::PeerConnected(PeerId(0)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_single_connection_event() {
|
||||
let settings: Option<Settings> = Some(Default::default());
|
||||
let addr = "127.0.0.1:56004".parse().unwrap();
|
||||
let host = Peer::host(addr, settings.clone()).unwrap();
|
||||
assert_eq!(host.shared.remote_peers.len(), 1);
|
||||
let peer1 = Peer::connect(addr, settings.clone()).unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
|
||||
assert_eq!(
|
||||
peer1.recv().next(),
|
||||
Some(NetworkEvent::PeerConnected(PeerId(0)))
|
||||
);
|
||||
assert_eq!(
|
||||
peer1.recv().next(),
|
||||
Some(NetworkEvent::PeerConnected(PeerId(1)))
|
||||
);
|
||||
|
||||
assert_eq!(peer1.recv().next(), None);
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_p2p() {
|
||||
let settings: Option<Settings> = Some(Default::default());
|
||||
let addr = "127.0.0.1:56005".parse().unwrap();
|
||||
let host = Peer::host(addr, settings.clone()).unwrap();
|
||||
assert_eq!(host.shared.remote_peers.len(), 1);
|
||||
let peer1 = Peer::connect(addr, settings.clone()).unwrap();
|
||||
let peer2 = Peer::connect(addr, settings.clone()).unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
assert_eq!(host.shared.remote_peers.len(), 3);
|
||||
|
||||
peer1
|
||||
.send(
|
||||
peer2.my_id().unwrap(),
|
||||
vec![123, 32, 51],
|
||||
Reliability::Reliable,
|
||||
)
|
||||
.unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
let events = peer2.recv().collect::<Vec<_>>();
|
||||
assert!(events.contains(&NetworkEvent::Message(Message {
|
||||
src: peer1.my_id().unwrap(),
|
||||
data: vec![123, 32, 51],
|
||||
})))
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_p2p_ipv6() {
|
||||
let settings: Option<Settings> = Some(Default::default());
|
||||
let addr = "[::1]:56006".parse().unwrap();
|
||||
let host = Peer::host(addr, settings.clone()).unwrap();
|
||||
assert_eq!(host.shared.remote_peers.len(), 1);
|
||||
let peer1 = Peer::connect(addr, settings.clone()).unwrap();
|
||||
let peer2 = Peer::connect(addr, settings.clone()).unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
assert_eq!(host.shared.remote_peers.len(), 3);
|
||||
|
||||
peer1
|
||||
.send(
|
||||
peer2.my_id().unwrap(),
|
||||
vec![123, 32, 51],
|
||||
Reliability::Reliable,
|
||||
)
|
||||
.unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
let events = peer2.recv().collect::<Vec<_>>();
|
||||
assert!(events.contains(&NetworkEvent::Message(Message {
|
||||
src: peer1.my_id().unwrap(),
|
||||
data: vec![123, 32, 51],
|
||||
})))
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_p2p_ipv6_2() {
|
||||
let settings: Option<Settings> = Some(Default::default());
|
||||
let baddr = "[::]:56007".parse().unwrap();
|
||||
let addr = "[::1]:56007".parse().unwrap();
|
||||
let addr2 = "127.0.0.1:56007".parse().unwrap();
|
||||
let host = Peer::host(baddr, settings.clone()).unwrap();
|
||||
assert_eq!(host.shared.remote_peers.len(), 1);
|
||||
let peer1 = Peer::connect(addr, settings.clone()).unwrap();
|
||||
let peer2 = Peer::connect(addr2, settings.clone()).unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
assert_eq!(host.shared.remote_peers.len(), 3);
|
||||
|
||||
peer1
|
||||
.send(
|
||||
peer2.my_id().unwrap(),
|
||||
vec![123, 32, 51],
|
||||
Reliability::Reliable,
|
||||
)
|
||||
.unwrap();
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
let events = peer2.recv().collect::<Vec<_>>();
|
||||
assert!(events.contains(&NetworkEvent::Message(Message {
|
||||
src: peer1.my_id().unwrap(),
|
||||
data: vec![123, 32, 51],
|
||||
})))
|
||||
}
|
||||
}
|
||||
145
noita_api/Cargo.lock
generated
145
noita_api/Cargo.lock
generated
|
|
@ -46,15 +46,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.2"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
|
|
@ -108,9 +108,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.2"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
|
|
@ -118,9 +118,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.30.5"
|
||||
version = "0.30.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85"
|
||||
checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
|
|
@ -157,19 +157,19 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
|
|
@ -178,6 +178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -232,18 +233,18 @@ checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
|
@ -291,18 +292,28 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -311,14 +322,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.143"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -331,6 +343,12 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
|
|
@ -360,9 +378,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -402,83 +420,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "2.1.1"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56"
|
||||
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
eyre = "0.6.12"
|
||||
libloading = "0.8.8"
|
||||
libloading = "0.8.9"
|
||||
noita_api_macro = {path = "noita_api_macro"}
|
||||
shared = {path = "../shared"}
|
||||
base64 = "0.22.1"
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ impl ComponentBuffer {
|
|||
});
|
||||
let com = heap::place_new(com);
|
||||
let index = self.component_list.len();
|
||||
self.component_list.push((com as *mut C).cast());
|
||||
self.component_list.push(com.cast());
|
||||
if self.entities.len() > index {
|
||||
self.entities[index] = entity;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ impl TextureInfo {
|
|||
Color {
|
||||
r: unsafe { buf.offset(tex_offset * 4 + 2).read() },
|
||||
g: unsafe { buf.offset(tex_offset * 4 + 1).read() },
|
||||
b: unsafe { buf.offset(tex_offset * 4 + 0).read() },
|
||||
b: unsafe { buf.offset(tex_offset * 4).read() },
|
||||
a: unsafe { buf.offset(tex_offset * 4 + 3).read() },
|
||||
}
|
||||
}
|
||||
|
|
@ -516,7 +516,7 @@ impl LiquidCell {
|
|||
unknown4: 0,
|
||||
unknown5: 0,
|
||||
unknown6: 0,
|
||||
color: color,
|
||||
color,
|
||||
original_color: color,
|
||||
lifetime,
|
||||
unknown8: 0,
|
||||
|
|
|
|||
41
shared/Cargo.lock
generated
41
shared/Cargo.lock
generated
|
|
@ -34,9 +34,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.2"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
|
|
@ -50,9 +50,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.30.5"
|
||||
version = "0.30.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85"
|
||||
checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
|
|
@ -80,36 +80,45 @@ checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -149,9 +158,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -191,6 +200,6 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue