mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef26a680fc | ||
|
|
62fd21e489 | ||
|
|
67e3f52a9d | ||
|
|
408feb6dd5 | ||
|
|
feb9e1db69 | ||
|
|
613705613b | ||
|
|
80a053c0a9 | ||
|
|
657455dae0 | ||
|
|
8ae35e12ee | ||
|
|
4e2c0e431b | ||
|
|
9c41f19eaf | ||
|
|
f75ce13f00 | ||
|
|
ac45a0a64b | ||
|
|
a8a73e28f4 | ||
|
|
e5983a9ec1 | ||
|
|
b7fa39d8cc | ||
|
|
3c30ed749c | ||
|
|
eb1a2ae9b4 | ||
|
|
ee0c31a8c3 | ||
|
|
4ab61b840a | ||
|
|
df29fa438a | ||
|
|
a3756c29bd | ||
|
|
4dc17f3aca | ||
|
|
d22280f177 |
@@ -47,8 +47,8 @@
|
||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
||||
<PackageVersion Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" />
|
||||
<PackageVersion Include="Pidgin" Version="3.3.0" />
|
||||
<PackageVersion Include="Robust.Natives" Version="0.2.1" />
|
||||
<PackageVersion Include="Robust.Natives.Zstd" Version="0.1.0-zstd1.5.7" />
|
||||
<PackageVersion Include="Robust.Natives" Version="0.2.3" />
|
||||
<PackageVersion Include="Robust.Natives.Zstd" Version="0.1.1-zstd1.5.7" />
|
||||
<PackageVersion Include="Robust.Natives.Cef" Version="131.3.5" />
|
||||
<PackageVersion Include="Robust.Shared.AuthLib" Version="0.1.2" />
|
||||
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.10" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -54,6 +54,38 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 267.3.2
|
||||
|
||||
|
||||
## 267.3.1
|
||||
|
||||
|
||||
## 267.3.0
|
||||
|
||||
### New features
|
||||
|
||||
* Sandbox:
|
||||
* Added `System.DateOnly` and `System.TimeOnly`.
|
||||
* `MapId`, `MapCoordinates`, and `EntityCoordinates` are now yaml serialisable
|
||||
* The base component tree lookup system has new methods including several new `QueryAabb()` overloads that take in a collection and various new `IntersectRay()` overloads that should replace `IntersectRayWithPredicate`.
|
||||
* Added `OccluderSystem.InRangeUnoccluded()` for checking for occluders that lie between two points.
|
||||
* `LocalizedCommands` now pass the command name as an argument to the localized help text.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed `MapLoaderSystem.SerializeEntitiesRecursive()` not properly serialising when given multiple root entities (e.g., multiple maps)
|
||||
* Fixed yaml hot reloading throwing invalid path exceptions.
|
||||
* The `EntityManager.CreateEntityUninitialized` overload that uses MapCoordinates now actually attaches entities to a grid if one is present at those coordinates, as was stated in it's documentation.
|
||||
* Fixed physics joint relays not being properly updated when an entity is removed from a container.
|
||||
|
||||
### Other
|
||||
|
||||
* Updated natives again to attempt to fix issues caused by the previous update.
|
||||
|
||||
|
||||
## 267.2.1
|
||||
|
||||
|
||||
## 267.2.0
|
||||
|
||||
### New features
|
||||
@@ -63,7 +95,7 @@ END TEMPLATE-->
|
||||
### Bugfixes
|
||||
|
||||
* Fixed `CollectionExtensions.TryGetValue` throwing an exception when given a negative list index.
|
||||
* Fixed `EntityManager.PredictedQueueDeleteEntity()` not deferring changes for networked entities until the end of the tick.
|
||||
* Fixed `EntityManager.PredictedQueueDeleteEntity()` not deferring changes for networked entities until the end of the tick.
|
||||
* Fixed `EntityManager.IsQueuedForDeletion` not returning true foe entities getting deleted via `PredictedQueueDeleteEntity()`
|
||||
|
||||
### Other
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Loc strings for various entity state & client-side PVS related commands
|
||||
|
||||
cmd-reset-ent-help = Usage: resetent <Entity UID>
|
||||
cmd-reset-ent-desc = Reset an entity to the most recently received server state. This will also reset entities that have been detached to null-space.
|
||||
cmd-reset-ent-help = Usage: {$command} <Entity UID>
|
||||
cmd-reset-ent-desc = Reset an entity to the most recently received server state. This will also reset entities that have been detached to null-space.
|
||||
|
||||
cmd-reset-all-ents-help = Usage: resetallents
|
||||
cmd-reset-all-ents-desc = Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
|
||||
cmd-reset-all-ents-help = Usage: {$command}
|
||||
cmd-reset-all-ents-desc = Resets all entities to the most recently received server state. This only impacts entities that have not been detached to null-space.
|
||||
|
||||
cmd-detach-ent-help = Usage: detachent <Entity UID>
|
||||
cmd-detach-ent-help = Usage: {$command} <Entity UID>
|
||||
cmd-detach-ent-desc = Detach an entity to null-space, as if it had left PVS range.
|
||||
|
||||
cmd-local-delete-help = Usage: localdelete <Entity UID>
|
||||
cmd-local-delete-help = Usage: {$command} <Entity UID>
|
||||
cmd-local-delete-desc = Deletes an entity. Unlike the normal delete command, this is CLIENT-SIDE. Unless the entity is a client-side entity, this will likely cause errors.
|
||||
|
||||
cmd-full-state-reset-help = Usage: fullstatereset
|
||||
cmd-full-state-reset-help = Usage: {$command}
|
||||
cmd-full-state-reset-desc = Discards any entity state information and requests a full-state from the server.
|
||||
|
||||
@@ -23,8 +23,8 @@ cmd-error-dir-not-found = Could not find directory: {$dir}.
|
||||
cmd-failure-no-attached-entity = There is no entity attached to this shell.
|
||||
|
||||
## 'help' command
|
||||
cmd-help-desc = Display general help or help text for a specific command
|
||||
cmd-help-help = Usage: help [command name]
|
||||
cmd-help-desc = Display general help or help text for a specific command.
|
||||
cmd-help-help = Usage: {$command} [command name]
|
||||
When no command name is provided, displays general-purpose help text. If a command name is provided, displays help text for that command.
|
||||
|
||||
cmd-help-no-args = To display help for a specific command, write 'help <command>'. To list all available commands, write 'list'. To search for commands, use 'list <filter>'.
|
||||
@@ -35,7 +35,7 @@ cmd-help-arg-cmdname = [command name]
|
||||
|
||||
## 'cvar' command
|
||||
cmd-cvar-desc = Gets or sets a CVar.
|
||||
cmd-cvar-help = Usage: cvar <name | ?> [value]
|
||||
cmd-cvar-help = Usage: {$command} <name | ?> [value]
|
||||
If a value is passed, the value is parsed and stored as the new value of the CVar.
|
||||
If not, the current value of the CVar is displayed.
|
||||
Use 'cvar ?' to get a list of all registered CVars.
|
||||
@@ -49,14 +49,14 @@ cmd-cvar-value-hidden = <value hidden>
|
||||
|
||||
## 'cvar_subs' command
|
||||
cmd-cvar_subs-desc = Lists the OnValueChanged subscriptions for a CVar.
|
||||
cmd-cvar_subs-help = Usage: cvar_subs <name>
|
||||
cmd-cvar_subs-help = Usage: {$command} <name>
|
||||
|
||||
cmd-cvar_subs-invalid-args = Must provide exactly one argument.
|
||||
cmd-cvar_subs-arg-name = <name>
|
||||
|
||||
## 'list' command
|
||||
cmd-list-desc = Lists available commands, with optional search filter
|
||||
cmd-list-help = Usage: list [filter]
|
||||
cmd-list-desc = Lists available commands, with optional search filter.
|
||||
cmd-list-help = Usage: {$command} [filter]
|
||||
Lists all available commands. If an argument is provided, it will be used to filter commands by name.
|
||||
|
||||
cmd-list-heading = SIDE NAME DESC{"\u000A"}-------------------------{"\u000A"}
|
||||
@@ -64,13 +64,13 @@ cmd-list-heading = SIDE NAME DESC{"\u000A"}-------------------------{
|
||||
cmd-list-arg-filter = [filter]
|
||||
|
||||
## '>' command, aka remote exec
|
||||
cmd-remoteexec-desc = Executes server-side commands
|
||||
cmd-remoteexec-desc = Executes server-side commands.
|
||||
cmd-remoteexec-help = Usage: > <command> [arg] [arg] [arg...]
|
||||
Executes a command on the server. This is necessary if a command with the same name exists on the client, as simply running the command would run the client command first.
|
||||
|
||||
## 'gc' command
|
||||
cmd-gc-desc = Run the GC (Garbage Collector)
|
||||
cmd-gc-help = Usage: gc [generation]
|
||||
cmd-gc-desc = Run the GC (Garbage Collector).
|
||||
cmd-gc-help = Usage: {$command} [generation]
|
||||
Uses GC.Collect() to execute the Garbage Collector.
|
||||
If an argument is provided, it is parsed as a GC generation number and GC.Collect(int) is used.
|
||||
Use the 'gfc' command to do an LOH-compacting full GC.
|
||||
@@ -79,13 +79,13 @@ cmd-gc-arg-generation = [generation]
|
||||
|
||||
## 'gcf' command
|
||||
cmd-gcf-desc = Run the GC, fully, compacting LOH and everything.
|
||||
cmd-gcf-help = Usage: gcf
|
||||
cmd-gcf-help = Usage: {$command}
|
||||
Does a full GC.Collect(2, GCCollectionMode.Forced, true, true) while also compacting LOH.
|
||||
This will probably lock up for hundreds of milliseconds, be warned.
|
||||
|
||||
## 'gc_mode' command
|
||||
cmd-gc_mode-desc = Change/Read the GC Latency mode
|
||||
cmd-gc_mode-help = Usage: gc_mode [type]
|
||||
cmd-gc_mode-desc = Change/Read the GC Latency mode.
|
||||
cmd-gc_mode-help = Usage: {$command} [type]
|
||||
If no argument is provided, returns the current GC latency mode.
|
||||
If an argument is passed, it is parsed as GCLatencyMode and set as the GC latency mode.
|
||||
|
||||
@@ -98,8 +98,8 @@ cmd-gc_mode-result = resulting gc latency mode: { $mode }
|
||||
cmd-gc_mode-arg-type = [type]
|
||||
|
||||
## 'mem' command
|
||||
cmd-mem-desc = Prints managed memory info
|
||||
cmd-mem-help = Usage: mem
|
||||
cmd-mem-desc = Prints managed memory info.
|
||||
cmd-mem-help = Usage: {$command}
|
||||
|
||||
cmd-mem-report = Heap Size: { TOSTRING($heapSize, "N0") }
|
||||
Total Allocated: { TOSTRING($totalAllocated, "N0") }
|
||||
@@ -108,26 +108,26 @@ cmd-mem-report = Heap Size: { TOSTRING($heapSize, "N0") }
|
||||
cmd-physics-overlay = {$overlay} is not a recognised overlay
|
||||
|
||||
## 'lsasm' command
|
||||
cmd-lsasm-desc = Lists loaded assemblies by load context
|
||||
cmd-lsasm-desc = Lists loaded assemblies by load context.
|
||||
cmd-lsasm-help = Usage: lsasm
|
||||
|
||||
## 'exec' command
|
||||
cmd-exec-desc = Executes a script file from the game's writeable user data
|
||||
cmd-exec-help = Usage: exec <fileName>
|
||||
cmd-exec-desc = Executes a script file from the game's writeable user data.
|
||||
cmd-exec-help = Usage: {$command} <fileName>
|
||||
Each line in the file is executed as a single command, unless it starts with a #
|
||||
|
||||
cmd-exec-arg-filename = <fileName>
|
||||
|
||||
## 'dump_net_comps' command
|
||||
cmd-dump_net_comps-desc = Prints the table of networked components.
|
||||
cmd-dump_net_comps-help = Usage: dump_net-comps
|
||||
cmd-dump_net_comps-help = Usage: {$command}
|
||||
|
||||
cmd-dump_net_comps-error-writeable = Registration still writeable, network ids have not been generated.
|
||||
cmd-dump_net_comps-header = Networked Component Registrations:
|
||||
|
||||
## 'dump_event_tables' command
|
||||
cmd-dump_event_tables-desc = Prints directed event tables for an entity.
|
||||
cmd-dump_event_tables-help = Usage: dump_event_tables <entityUid>
|
||||
cmd-dump_event_tables-help = Usage: {$command} <entityUid>
|
||||
|
||||
cmd-dump_event_tables-missing-arg-entity = Missing entity argument
|
||||
cmd-dump_event_tables-error-entity = Invalid entity
|
||||
@@ -135,7 +135,7 @@ cmd-dump_event_tables-arg-entity = <entityUid>
|
||||
|
||||
## 'monitor' command
|
||||
cmd-monitor-desc = Toggles a debug monitor in the F3 menu.
|
||||
cmd-monitor-help = Usage: monitor <name>
|
||||
cmd-monitor-help = Usage: {$command} <name>
|
||||
Possible monitors are: { $monitors }
|
||||
You can also use the special values "-all" and "+all" to hide or show all monitors, respectively.
|
||||
|
||||
@@ -148,13 +148,13 @@ cmd-monitor-plus-all-hint = Shows all monitors
|
||||
|
||||
## 'setambientlight' command
|
||||
cmd-set-ambient-light-desc = Allows you to set the ambient light for the specified map, in SRGB.
|
||||
cmd-set-ambient-light-help = setambientlight [mapid] [r g b a]
|
||||
cmd-set-ambient-light-help = Usage: {$command} [mapid] [r g b a]
|
||||
cmd-set-ambient-light-parse = Unable to parse args as a byte values for a color.
|
||||
|
||||
## Mapping commands
|
||||
|
||||
cmd-savemap-desc = Serializes a map to disk. Will not save a post-init map unless forced.
|
||||
cmd-savemap-help = savemap <MapID> <Path> [force]
|
||||
cmd-savemap-help = Usage: {$command} <MapID> <Path> [force]
|
||||
cmd-savemap-not-exist = Target map does not exist.
|
||||
cmd-savemap-init-warning = Attempted to save a post-init map without forcing the save.
|
||||
cmd-savemap-attempt = Attempting to save map {$mapId} to {$path}.
|
||||
@@ -165,7 +165,7 @@ cmd-hint-savemap-path = <Path>
|
||||
cmd-hint-savemap-force = [bool]
|
||||
|
||||
cmd-loadmap-desc = Loads a map from disk into the game.
|
||||
cmd-loadmap-help = loadmap <MapID> <Path> [x] [y] [rotation] [consistentUids]
|
||||
cmd-loadmap-help = Usage: {$command} <MapID> <Path> [x] [y] [rotation] [consistentUids]
|
||||
cmd-loadmap-nullspace = You cannot load into map 0.
|
||||
cmd-loadmap-exists = Map {$mapId} already exists.
|
||||
cmd-loadmap-success = Map {$mapId} has been loaded from {$path}.
|
||||
@@ -180,73 +180,74 @@ cmd-hint-savebp-id = <Grid EntityID>
|
||||
## 'flushcookies' command
|
||||
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
|
||||
|
||||
cmd-flushcookies-desc = Flush CEF cookie storage to disk
|
||||
cmd-flushcookies-help = This ensure cookies are properly saved to disk in the event of unclean shutdowns.
|
||||
cmd-flushcookies-desc = Flush CEF cookie storage to disk.
|
||||
cmd-flushcookies-help = Usage: {$command}
|
||||
This ensure cookies are properly saved to disk in the event of unclean shutdowns.
|
||||
Note that the actual operation is asynchronous.
|
||||
|
||||
cmd-ldrsc-desc = Pre-caches a resource.
|
||||
cmd-ldrsc-help = Usage: ldrsc <path> <type>
|
||||
cmd-ldrsc-help = Usage: {$command} <path> <type>
|
||||
|
||||
cmd-rldrsc-desc = Reloads a resource.
|
||||
cmd-rldrsc-help = Usage: rldrsc <path> <type>
|
||||
cmd-rldrsc-help = Usage: {$command} <path> <type>
|
||||
|
||||
cmd-gridtc-desc = Gets the tile count of a grid.
|
||||
cmd-gridtc-help = Usage: gridtc <gridId>
|
||||
cmd-gridtc-help = Usage: {$command} <gridId>
|
||||
|
||||
|
||||
# Client-side commands
|
||||
cmd-guidump-desc = Dump GUI tree to /guidump.txt in user data.
|
||||
cmd-guidump-help = Usage: guidump
|
||||
cmd-guidump-help = Usage: {$command}
|
||||
|
||||
cmd-uitest-desc = Open a dummy UI testing window
|
||||
cmd-uitest-help = Usage: uitest
|
||||
cmd-uitest-desc = Open a dummy UI testing window.
|
||||
cmd-uitest-help = Usage: {$command}
|
||||
|
||||
## 'uitest2' command
|
||||
cmd-uitest2-desc = Opens a UI control testing OS window
|
||||
cmd-uitest2-help = Usage: uitest2 <tab>
|
||||
cmd-uitest2-desc = Opens a UI control testing OS window.
|
||||
cmd-uitest2-help = Usage: {$command} <tab>
|
||||
cmd-uitest2-arg-tab = <tab>
|
||||
cmd-uitest2-error-args = Expected at most one argument
|
||||
cmd-uitest2-error-tab = Invalid tab: '{$value}'
|
||||
cmd-uitest2-title = UITest2
|
||||
|
||||
|
||||
cmd-setclipboard-desc = Sets the system clipboard
|
||||
cmd-setclipboard-help = Usage: setclipboard <text>
|
||||
cmd-setclipboard-desc = Sets the system clipboard.
|
||||
cmd-setclipboard-help = Usage: {$command} <text>
|
||||
|
||||
cmd-getclipboard-desc = Gets the system clipboard
|
||||
cmd-getclipboard-help = Usage: Getclipboard
|
||||
cmd-getclipboard-desc = Gets the system clipboard.
|
||||
cmd-getclipboard-help = Usage: {$command}
|
||||
|
||||
cmd-togglelight-desc = Toggles light rendering.
|
||||
cmd-togglelight-help = Usage: togglelight
|
||||
cmd-togglelight-help = Usage: {$command}
|
||||
|
||||
cmd-togglefov-desc = Toggles fov for client.
|
||||
cmd-togglefov-help = Usage: togglefov
|
||||
cmd-togglefov-help = Usage: {$command}
|
||||
|
||||
cmd-togglehardfov-desc = Toggles hard fov for client. (for debugging space-station-14#2353)
|
||||
cmd-togglehardfov-help = Usage: togglehardfov
|
||||
cmd-togglehardfov-help = Usage: {$command}
|
||||
|
||||
cmd-toggleshadows-desc = Toggles shadow rendering.
|
||||
cmd-toggleshadows-help = Usage: toggleshadows
|
||||
cmd-toggleshadows-help = Usage: {$command}
|
||||
|
||||
cmd-togglelightbuf-desc = Toggles lighting rendering. This includes shadows but not FOV.
|
||||
cmd-togglelightbuf-help = Usage: togglelightbuf
|
||||
cmd-togglelightbuf-help = Usage: {$command}
|
||||
|
||||
cmd-chunkinfo-desc = Gets info about a chunk under your mouse cursor.
|
||||
cmd-chunkinfo-help = Usage: chunkinfo
|
||||
cmd-chunkinfo-help = Usage: {$command}
|
||||
|
||||
cmd-rldshader-desc = Reloads all shaders.
|
||||
cmd-rldshader-help = Usage: rldshader
|
||||
cmd-rldshader-help = Usage: {$command}
|
||||
|
||||
cmd-cldbglyr-desc = Toggle fov and light debug layers.
|
||||
cmd-cldbglyr-help= Usage: cldbglyr <layer>: Toggle <layer>
|
||||
cmd-cldbglyr-help= Usage: {$command} <layer>: Toggle <layer>
|
||||
cldbglyr: Turn all Layers off
|
||||
|
||||
cmd-key-info-desc = Keys key info for a key.
|
||||
cmd-key-info-help = Usage: keyinfo <Key>
|
||||
cmd-key-info-help = Usage: {$command} <Key>
|
||||
|
||||
## 'bind' command
|
||||
cmd-bind-desc = Binds an input key combination to an input command.
|
||||
cmd-bind-help = Usage: bind { cmd-bind-arg-key } { cmd-bind-arg-mode } { cmd-bind-arg-command }
|
||||
cmd-bind-help = Usage: {$command} { cmd-bind-arg-key } { cmd-bind-arg-mode } { cmd-bind-arg-command }
|
||||
Note that this DOES NOT automatically save bindings.
|
||||
Use the 'svbind' command to save binding configuration.
|
||||
|
||||
@@ -255,316 +256,322 @@ cmd-bind-arg-mode = <BindMode>
|
||||
cmd-bind-arg-command = <InputCommand>
|
||||
|
||||
cmd-net-draw-interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net-draw-interp-help = Usage: net_draw_interp
|
||||
cmd-net-draw-interp-help = Usage: {$command}
|
||||
|
||||
cmd-net-watch-ent-desc = Dumps all network updates for an EntityId to the console.
|
||||
cmd-net-watch-ent-help = Usage: net_watchent <0|EntityUid>
|
||||
cmd-net-watch-ent-help = Usage: {$command} <0|EntityUid>
|
||||
|
||||
cmd-net-refresh-desc = Requests a full server state.
|
||||
cmd-net-refresh-help = Usage: net_refresh
|
||||
cmd-net-refresh-help = Usage: {$command}
|
||||
|
||||
cmd-net-entity-report-desc = Toggles the net entity report panel.
|
||||
cmd-net-entity-report-help = Usage: net_entityreport
|
||||
cmd-net-entity-report-help = Usage: {$command}
|
||||
|
||||
cmd-fill-desc = Fill up the console for debugging.
|
||||
cmd-fill-help = Fills the console with some nonsense for debugging.
|
||||
cmd-fill-help = Usage: {$command}
|
||||
Fills the console with some nonsense for debugging.
|
||||
|
||||
cmd-cls-desc = Clears the console.
|
||||
cmd-cls-help = Clears the debug console of all messages.
|
||||
cmd-cls-help = Usage: {$command}
|
||||
Clears the debug console of all messages.
|
||||
|
||||
cmd-sendgarbage-desc = Sends garbage to the server.
|
||||
cmd-sendgarbage-help = The server will reply with 'no u'
|
||||
cmd-sendgarbage-help = Usage: {$command}
|
||||
The server will reply with 'no u'
|
||||
|
||||
cmd-loadgrid-desc = Loads a grid from a file into an existing map.
|
||||
cmd-loadgrid-help = loadgrid <MapID> <Path> [x y] [rotation] [storeUids]
|
||||
cmd-loadgrid-help = Usage: {$command} <MapID> <Path> [x y] [rotation] [storeUids]
|
||||
|
||||
cmd-loc-desc = Prints the absolute location of the player's entity to console.
|
||||
cmd-loc-help = loc
|
||||
cmd-loc-help = Usage: {$command}
|
||||
|
||||
cmd-tpgrid-desc = Teleports a grid to a new location.
|
||||
cmd-tpgrid-help = tpgrid <gridId> <X> <Y> [<MapId>]
|
||||
cmd-tpgrid-help = Usage: {$command} <gridId> <X> <Y> [<MapId>]
|
||||
|
||||
cmd-rmgrid-desc = Removes a grid from a map. You cannot remove the default grid.
|
||||
cmd-rmgrid-help = rmgrid <gridId>
|
||||
cmd-rmgrid-help = Usage: {$command} <gridId>
|
||||
|
||||
cmd-mapinit-desc = Runs map init on a map.
|
||||
cmd-mapinit-help = mapinit <mapID>
|
||||
cmd-mapinit-help = Usage: {$command} <mapID>
|
||||
|
||||
cmd-lsmap-desc = Lists maps.
|
||||
cmd-lsmap-help = lsmap
|
||||
cmd-lsmap-help = Usage: {$command}
|
||||
|
||||
cmd-lsgrid-desc = Lists grids.
|
||||
cmd-lsgrid-help = lsgrid
|
||||
cmd-lsgrid-help = Usage: {$command}
|
||||
|
||||
cmd-addmap-desc = Adds a new empty map to the round. If the mapID already exists, this command does nothing.
|
||||
cmd-addmap-help = addmap <mapID> [pre-init]
|
||||
cmd-addmap-help = Usage: {$command} <mapID> [pre-init]
|
||||
|
||||
cmd-rmmap-desc = Removes a map from the world. You cannot remove nullspace.
|
||||
cmd-rmmap-help = rmmap <mapId>
|
||||
cmd-rmmap-help = Usage: {$command} <mapId>
|
||||
|
||||
cmd-savegrid-desc = Serializes a grid to disk.
|
||||
cmd-savegrid-help = savegrid <gridID> <Path>
|
||||
cmd-savegrid-help = Usage: {$command} <gridID> <Path>
|
||||
|
||||
cmd-testbed-desc = Loads a physics testbed on the specified map.
|
||||
cmd-testbed-help = testbed <mapid> <test>
|
||||
cmd-testbed-help = Usage: {$command} <mapid> <test>
|
||||
|
||||
## 'flushcookies' command
|
||||
# Note: the flushcookies command is from Robust.Client.WebView, it's not in the main engine code.
|
||||
|
||||
## 'addcomp' command
|
||||
cmd-addcomp-desc = Adds a component to an entity.
|
||||
cmd-addcomp-help = addcomp <uid> <componentName>
|
||||
cmd-addcomp-help = Usage: {$command} <uid> <componentName>
|
||||
cmd-addcompc-desc = Adds a component to an entity on the client.
|
||||
cmd-addcompc-help = addcompc <uid> <componentName>
|
||||
cmd-addcompc-help = Usage: {$command} <uid> <componentName>
|
||||
|
||||
## 'rmcomp' command
|
||||
cmd-rmcomp-desc = Removes a component from an entity.
|
||||
cmd-rmcomp-help = rmcomp <uid> <componentName>
|
||||
cmd-rmcomp-help = Usage: {$command} <uid> <componentName>
|
||||
cmd-rmcompc-desc = Removes a component from an entity on the client.
|
||||
cmd-rmcompc-help = rmcomp <uid> <componentName>
|
||||
cmd-rmcompc-help = Usage: {$command} <uid> <componentName>
|
||||
|
||||
## 'addview' command
|
||||
cmd-addview-desc = Allows you to subscribe to an entity's view for debugging purposes.
|
||||
cmd-addview-help = addview <entityUid>
|
||||
cmd-addview-help = Usage: {$command} <entityUid>
|
||||
cmd-addviewc-desc = Allows you to subscribe to an entity's view for debugging purposes.
|
||||
cmd-addviewc-help = addview <entityUid>
|
||||
cmd-addviewc-help = Usage: {$command} <entityUid>
|
||||
|
||||
## 'removeview' command
|
||||
cmd-removeview-desc = Allows you to unsubscribe to an entity's view for debugging purposes.
|
||||
cmd-removeview-help = removeview <entityUid>
|
||||
cmd-removeview-help = Usage: {$command} <entityUid>
|
||||
|
||||
## 'loglevel' command
|
||||
cmd-loglevel-desc = Changes the log level for a provided sawmill.
|
||||
cmd-loglevel-help = Usage: loglevel <sawmill> <level>
|
||||
cmd-loglevel-help = Usage: {$command} <sawmill> <level>
|
||||
sawmill: A label prefixing log messages. This is the one you're setting the level for.
|
||||
level: The log level. Must match one of the values of the LogLevel enum.
|
||||
|
||||
cmd-testlog-desc = Writes a test log to a sawmill.
|
||||
cmd-testlog-help = Usage: testlog <sawmill> <level> <message>
|
||||
cmd-testlog-help = Usage: {$command} <sawmill> <level> <message>
|
||||
sawmill: A label prefixing the logged message.
|
||||
level: The log level. Must match one of the values of the LogLevel enum.
|
||||
message: The message to be logged. Wrap this in double quotes if you want to use spaces.
|
||||
|
||||
## 'vv' command
|
||||
cmd-vv-desc = Opens View Variables.
|
||||
cmd-vv-help = Usage: vv <entity ID|IoC interface name|SIoC interface name>
|
||||
cmd-vv-help = Usage: {$command} <entity ID|IoC interface name|SIoC interface name>
|
||||
|
||||
## 'showvelocities' command
|
||||
cmd-showvelocities-desc = Displays your angular and linear velocities.
|
||||
cmd-showvelocities-help = Usage: showvelocities
|
||||
cmd-showvelocities-help = Usage: {$command}
|
||||
|
||||
## 'setinputcontext' command
|
||||
cmd-setinputcontext-desc = Sets the active input context.
|
||||
cmd-setinputcontext-help = Usage: setinputcontext <context>
|
||||
cmd-setinputcontext-help = Usage: {$command} <context>
|
||||
|
||||
## 'forall' command
|
||||
cmd-forall-desc = Runs a command over all entities with a given component.
|
||||
cmd-forall-help = Usage: forall <bql query> do <command...>
|
||||
cmd-forall-help = Usage: {$command} <bql query> do <command...>
|
||||
|
||||
## 'delete' command
|
||||
cmd-delete-desc = Deletes the entity with the specified ID.
|
||||
cmd-delete-help = delete <entity UID>
|
||||
cmd-delete-help = Usage: {$command} <entity UID>
|
||||
|
||||
# System commands
|
||||
cmd-showtime-desc = Shows the server time.
|
||||
cmd-showtime-help = showtime
|
||||
cmd-showtime-help = Usage: {$command}
|
||||
|
||||
cmd-restart-desc = Gracefully restarts the server (not just the round).
|
||||
cmd-restart-help = restart
|
||||
cmd-restart-help = Usage: {$command}
|
||||
|
||||
cmd-shutdown-desc = Gracefully shuts down the server.
|
||||
cmd-shutdown-help = shutdown
|
||||
cmd-shutdown-help = Usage: {$command}
|
||||
|
||||
cmd-saveconfig-desc = Saves the server configuration to the config file.
|
||||
cmd-saveconfig-help = saveconfig
|
||||
cmd-saveconfig-help = Usage: {$command}
|
||||
|
||||
cmd-netaudit-desc = Prints into about NetMsg security.
|
||||
cmd-netaudit-help = netaudit
|
||||
cmd-netaudit-help = Usage: {$command}
|
||||
|
||||
# Player commands
|
||||
cmd-tp-desc = Teleports a player to any location in the round.
|
||||
cmd-tp-help = tp <x> <y> [<mapID>]
|
||||
cmd-tp-help = Usage: {$command} <x> <y> [<mapID>]
|
||||
|
||||
cmd-tpto-desc = Teleports the current player or the specified players/entities to the location of the first player/entity.
|
||||
cmd-tpto-help = tpto <username|uid> [username|NetEntity]...
|
||||
cmd-tpto-help = Usage: {$command} <username|uid> [username|NetEntity]...
|
||||
cmd-tpto-destination-hint = destination (NetEntity or username)
|
||||
cmd-tpto-victim-hint = entity to teleport (NetEntity or username)
|
||||
cmd-tpto-parse-error = Cant resolve entity or player: {$str}
|
||||
|
||||
cmd-listplayers-desc = Lists all players currently connected.
|
||||
cmd-listplayers-help = listplayers
|
||||
cmd-listplayers-help = Usage: {$command}
|
||||
|
||||
cmd-kick-desc = Kicks a connected player out of the server, disconnecting them.
|
||||
cmd-kick-help = kick <PlayerIndex> [<Reason>]
|
||||
cmd-kick-help = Usage: {$command} <PlayerIndex> [<Reason>]
|
||||
|
||||
# Spin command
|
||||
cmd-spin-desc = Causes an entity to spin. Default entity is the attached player's parent.
|
||||
cmd-spin-help = spin velocity [drag] [entityUid]
|
||||
cmd-spin-help = Usage: {$command} velocity [drag] [entityUid]
|
||||
|
||||
# Localization command
|
||||
cmd-rldloc-desc = Reloads localization (client & server).
|
||||
cmd-rldloc-help = Usage: rldloc
|
||||
cmd-rldloc-help = Usage: {$command}
|
||||
|
||||
# Debug entity controls
|
||||
cmd-spawn-desc = Spawns an entity with specific type.
|
||||
cmd-spawn-help = spawn <prototype> OR spawn <prototype> <relative entity ID> OR spawn <prototype> <x> <y>
|
||||
cmd-spawn-help = Usage: {$command} <prototype> | {$command} <prototype> <relative entity ID> | {$command} <prototype> <x> <y>
|
||||
cmd-cspawn-desc = Spawns a client-side entity with specific type at your feet.
|
||||
cmd-cspawn-help = cspawn <entity type>
|
||||
cmd-cspawn-help = Usage: {$command} <entity type>
|
||||
|
||||
cmd-dumpentities-desc = Dump entity list.
|
||||
cmd-dumpentities-help = Dumps entity list of UIDs and prototype.
|
||||
cmd-dumpentities-help = Usage: {$command}
|
||||
Dumps entity list of UIDs and prototype.
|
||||
|
||||
cmd-getcomponentregistration-desc = Gets component registration information.
|
||||
cmd-getcomponentregistration-help = Usage: getcomponentregistration <componentName>
|
||||
cmd-getcomponentregistration-help = Usage: {$command} <componentName>
|
||||
|
||||
cmd-showrays-desc = Toggles debug drawing of physics rays. An integer for <raylifetime> must be provided.
|
||||
cmd-showrays-help = Usage: showrays <raylifetime>
|
||||
cmd-showrays-help = Usage: {$command} <raylifetime>
|
||||
|
||||
cmd-disconnect-desc = Immediately disconnect from the server and go back to the main menu.
|
||||
cmd-disconnect-help = Usage: disconnect
|
||||
cmd-disconnect-help = Usage: {$command}
|
||||
|
||||
cmd-entfo-desc = Displays verbose diagnostics for an entity.
|
||||
cmd-entfo-help = Usage: entfo <entityuid>
|
||||
cmd-entfo-help = Usage: {$command} <entityuid>
|
||||
The entity UID can be prefixed with 'c' to convert it to a client entity UID.
|
||||
|
||||
cmd-fuck-desc = Throws an exception
|
||||
cmd-fuck-help = Usage: fuck
|
||||
cmd-fuck-desc = Throws an exception.
|
||||
cmd-fuck-help = Usage: {$command}
|
||||
|
||||
cmd-showpos-desc = Show the position of all entities on the screen.
|
||||
cmd-showpos-help = Usage: showpos
|
||||
cmd-showpos-help = Usage: {$command}
|
||||
|
||||
cmd-showrot-desc = Show the rotation of all entities on the screen.
|
||||
cmd-showrot-help = Usage: showrot
|
||||
cmd-showrot-help = Usage: {$command}
|
||||
|
||||
cmd-showvel-desc = Show the local velocity of all entites on the screen.
|
||||
cmd-showvel-help = Usage: showvel
|
||||
cmd-showvel-help = Usage: {$command}
|
||||
|
||||
cmd-showangvel-desc = Show the angular velocity of all entities on the screen.
|
||||
cmd-showangvel-help = Usage: showangvel
|
||||
cmd-showangvel-help = Usage: {$command}
|
||||
|
||||
cmd-sggcell-desc = Lists entities on a snap grid cell.
|
||||
cmd-sggcell-help = Usage: sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
|
||||
cmd-sggcell-help = Usage: {$command} <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
|
||||
|
||||
cmd-overrideplayername-desc = Changes the name used when attempting to connect to the server.
|
||||
cmd-overrideplayername-help = Usage: overrideplayername <name>
|
||||
cmd-overrideplayername-help = Usage: {$command} <name>
|
||||
|
||||
cmd-showanchored-desc = Shows anchored entities on a particular tile
|
||||
cmd-showanchored-help = Usage: showanchored
|
||||
cmd-showanchored-desc = Shows anchored entities on a particular tile.
|
||||
cmd-showanchored-help = Usage: {$command}
|
||||
|
||||
cmd-dmetamem-desc = Dumps a type's members in a format suitable for the sandbox configuration file.
|
||||
cmd-dmetamem-help = Usage: dmetamem <type>
|
||||
cmd-dmetamem-help = Usage: {$command} <type>
|
||||
|
||||
cmd-launchauth-desc = Load authentication tokens from launcher data to aid in testing of live servers.
|
||||
cmd-launchauth-help = Usage: launchauth <account name>
|
||||
cmd-launchauth-help = Usage: {$command} <account name>
|
||||
|
||||
cmd-lightbb-desc = Toggles whether to show light bounding boxes.
|
||||
cmd-lightbb-help = Usage: lightbb
|
||||
cmd-lightbb-help = Usage: {$command}
|
||||
|
||||
cmd-monitorinfo-desc = Monitors info
|
||||
cmd-monitorinfo-help = Usage: monitorinfo <id>
|
||||
cmd-monitorinfo-desc = Monitors info.
|
||||
cmd-monitorinfo-help = Usage: {$command} <id>
|
||||
|
||||
cmd-setmonitor-desc = Set monitor
|
||||
cmd-setmonitor-help = Usage: setmonitor <id>
|
||||
cmd-setmonitor-desc = Set monitor.
|
||||
cmd-setmonitor-help = Usage: {$command} <id>
|
||||
|
||||
cmd-physics-desc = Shows a debug physics overlay. The arg supplied specifies the overlay.
|
||||
cmd-physics-help = Usage: physics <aabbs / com / contactnormals / contactpoints / distance / joints / shapeinfo / shapes>
|
||||
cmd-physics-help = Usage: {$command} <aabbs / com / contactnormals / contactpoints / distance / joints / shapeinfo / shapes>
|
||||
|
||||
cmd-hardquit-desc = Kills the game client instantly.
|
||||
cmd-hardquit-help = Kills the game client instantly, leaving no traces. No telling the server goodbye.
|
||||
cmd-hardquit-help = Usage: {$command}
|
||||
Kills the game client instantly, leaving no traces. No telling the server goodbye.
|
||||
|
||||
cmd-quit-desc = Shuts down the game client gracefully.
|
||||
cmd-quit-help = Properly shuts down the game client, notifying the connected server and such.
|
||||
cmd-quit-help = Usage: {$command}
|
||||
Properly shuts down the game client, notifying the connected server and such.
|
||||
|
||||
cmd-csi-desc = Opens a C# interactive console.
|
||||
cmd-csi-help = Usage: csi
|
||||
cmd-csi-help = Usage: {$command}
|
||||
|
||||
cmd-scsi-desc = Opens a C# interactive console on the server.
|
||||
cmd-scsi-help = Usage: scsi
|
||||
cmd-scsi-help = Usage: {$command}
|
||||
|
||||
cmd-watch-desc = Opens a variable watch window.
|
||||
cmd-watch-help = Usage: watch
|
||||
cmd-watch-help = Usage: {$command}
|
||||
|
||||
cmd-showspritebb-desc = Toggle whether sprite bounds are shown
|
||||
cmd-showspritebb-help = Usage: showspritebb
|
||||
cmd-showspritebb-desc = Toggle whether sprite bounds are shown.
|
||||
cmd-showspritebb-help = Usage: {$command}
|
||||
|
||||
cmd-togglelookup-desc = Shows / hides entitylookup bounds via an overlay.
|
||||
cmd-togglelookup-help = Usage: togglelookup
|
||||
cmd-togglelookup-help = Usage: {$command}
|
||||
|
||||
cmd-net_entityreport-desc = Toggles the net entity report panel.
|
||||
cmd-net_entityreport-help = Usage: net_entityreport
|
||||
cmd-net_entityreport-help = Usage: {$command}
|
||||
|
||||
cmd-net_refresh-desc = Requests a full server state.
|
||||
cmd-net_refresh-help = Usage: net_refresh
|
||||
cmd-net_refresh-help = Usage: {$command}
|
||||
|
||||
cmd-net_graph-desc = Toggles the net statistics panel.
|
||||
cmd-net_graph-help = Usage: net_graph
|
||||
cmd-net_graph-help = Usage: {$command}
|
||||
|
||||
cmd-net_watchent-desc = Dumps all network updates for an EntityId to the console.
|
||||
cmd-net_watchent-help = Usage: net_watchent <0|EntityUid>
|
||||
cmd-net_watchent-help = Usage: {$command} <0|EntityUid>
|
||||
|
||||
cmd-net_draw_interp-desc = Toggles the debug drawing of the network interpolation.
|
||||
cmd-net_draw_interp-help = Usage: net_draw_interp <0|EntityUid>
|
||||
cmd-net_draw_interp-help = Usage: {$command} <0|EntityUid>
|
||||
|
||||
cmd-vram-desc = Displays video memory usage statics by the game.
|
||||
cmd-vram-help = Usage: vram
|
||||
cmd-vram-help = Usage: {$command}
|
||||
|
||||
cmd-showislands-desc = Shows the current physics bodies involved in each physics island.
|
||||
cmd-showislands-help = Usage: showislands
|
||||
cmd-showislands-help = Usage: {$command}
|
||||
|
||||
cmd-showgridnodes-desc = Shows the nodes for grid split purposes.
|
||||
cmd-showgridnodes-help = Usage: showgridnodes
|
||||
cmd-showgridnodes-help = Usage: {$command}
|
||||
|
||||
cmd-profsnap-desc = Make a profiling snapshot.
|
||||
cmd-profsnap-help = Usage: profsnap
|
||||
cmd-profsnap-help = Usage: {$command}
|
||||
|
||||
cmd-devwindow-desc = Dev Window
|
||||
cmd-devwindow-help = Usage: devwindow
|
||||
cmd-devwindow-desc = Dev Window.
|
||||
cmd-devwindow-help = Usage: {$command}
|
||||
|
||||
cmd-scene-desc = Immediately changes the UI scene/state.
|
||||
cmd-scene-help = Usage: scene <className>
|
||||
cmd-scene-help = Usage: {$command} <className>
|
||||
|
||||
cmd-szr_stats-desc = Report serializer statistics.
|
||||
cmd-szr_stats-help = Usage: szr_stats
|
||||
cmd-szr_stats-help = Usage: {$command}
|
||||
|
||||
cmd-hwid-desc = Returns the current HWID (HardWare ID).
|
||||
cmd-hwid-help = Usage: hwid
|
||||
cmd-hwid-help = Usage: {$command}
|
||||
|
||||
cmd-vvread-desc = Retrieve a path's value using VV (View Variables).
|
||||
cmd-vvread-help = Usage: vvread <path>
|
||||
cmd-vvread-help = Usage: {$command} <path>
|
||||
|
||||
cmd-vvwrite-desc = Modify a path's value using VV (View Variables).
|
||||
cmd-vvwrite-help = Usage: vvwrite <path>
|
||||
cmd-vvwrite-help = Usage: {$command} <path>
|
||||
|
||||
cmd-vvinvoke-desc = Invoke/Call a path with arguments using VV.
|
||||
cmd-vvinvoke-help = Usage: vvinvoke <path> [arguments...]
|
||||
cmd-vvinvoke-help = Usage: {$command} <path> [arguments...]
|
||||
|
||||
cmd-dump_dependency_injectors-desc = Dump IoCManager's dependency injector cache.
|
||||
cmd-dump_dependency_injectors-help = Usage: dump_dependency_injectors
|
||||
cmd-dump_dependency_injectors-help = Usage: {$command}
|
||||
cmd-dump_dependency_injectors-total-count = Total count: { $total }
|
||||
|
||||
cmd-dump_netserializer_type_map-desc = Dump NetSerializer's type map and serializer hash.
|
||||
cmd-dump_netserializer_type_map-help = Usage: dump_netserializer_type_map
|
||||
cmd-dump_netserializer_type_map-help = Usage: {$command}
|
||||
|
||||
cmd-hub_advertise_now-desc = Immediately advertise to the master hub server
|
||||
cmd-hub_advertise_now-help = Usage: hub_advertise_now
|
||||
cmd-hub_advertise_now-desc = Immediately advertise to the master hub server.
|
||||
cmd-hub_advertise_now-help = Usage: {$command}
|
||||
|
||||
cmd-echo-desc = Echo arguments back to the console
|
||||
cmd-echo-help = Usage: echo "<message>"
|
||||
cmd-echo-desc = Echo arguments back to the console.
|
||||
cmd-echo-help = Usage: {$command} "<message>"
|
||||
|
||||
## 'vfs_ls' command
|
||||
cmd-vfs_ls-desc = List directory contents in the VFS.
|
||||
cmd-vfs_ls-help = Usage: vfs_list <path>
|
||||
cmd-vfs_ls-help = Usage: {$command} <path>
|
||||
Example:
|
||||
vfs_list /Assemblies
|
||||
|
||||
cmd-vfs_ls-err-args = Need exactly 1 argument.
|
||||
cmd-vfs_ls-hint-path = <path>
|
||||
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites
|
||||
cmd-reloadtiletextures-help = Usage: reloadtiletextures
|
||||
cmd-reloadtiletextures-desc = Reloads the tile texture atlas to allow hot reloading tile sprites.
|
||||
cmd-reloadtiletextures-help = Usage: {$command}
|
||||
|
||||
cmd-audio_length-desc = Shows the length of an audio file
|
||||
cmd-audio_length-help = Usage: audio_length { cmd-audio_length-arg-file-name }
|
||||
cmd-audio_length-help = Usage: {$command} { cmd-audio_length-arg-file-name }
|
||||
cmd-audio_length-arg-file-name = <file name>
|
||||
|
||||
## PVS
|
||||
@@ -573,8 +580,8 @@ cmd-pvs-override-info-empty = Entity {$nuid} has no PVS overrides.
|
||||
cmd-pvs-override-info-global = Entity {$nuid} has a global override.
|
||||
cmd-pvs-override-info-clients = Entity {$nuid} has a session override for {$clients}.
|
||||
|
||||
cmd-localization_set_culture-desc = Set DefaultCulture for the client LocalizationManager
|
||||
cmd-localization_set_culture-help = Usage: localization_set_culture <cultureName>
|
||||
cmd-localization_set_culture-desc = Set DefaultCulture for the client LocalizationManager.
|
||||
cmd-localization_set_culture-help = Usage: {$command} <cultureName>
|
||||
cmd-localization_set_culture-culture-name = <cultureName>
|
||||
cmd-localization_set_culture-changed = Localization changed to { $code } ({ $nativeName } / { $englishName })
|
||||
|
||||
|
||||
@@ -428,3 +428,7 @@ command-description-cmd-info =
|
||||
On its own, this means it'll print the command's help message.
|
||||
command-description-comp-rm =
|
||||
Removes the given component from the entity.
|
||||
|
||||
command-description-overlay-toggle = Toggle an overlay on or off
|
||||
command-description-overlay-add = Add an overlay (if it does not already exist)
|
||||
command-description-overlay-remove = Remove an overlay
|
||||
|
||||
@@ -31,8 +31,7 @@ public sealed class LightTreeSystem : ComponentTreeSystem<LightTreeComponent, Po
|
||||
|
||||
var pos = XformSystem.GetRelativePosition(
|
||||
entry.Transform,
|
||||
entry.Component.TreeUid.Value,
|
||||
GetEntityQuery<TransformComponent>());
|
||||
entry.Component.TreeUid.Value);
|
||||
|
||||
return ExtractAabb(in entry, pos, default);
|
||||
}
|
||||
|
||||
53
Robust.Client/Debugging/OverlayCommand.cs
Normal file
53
Robust.Client/Debugging/OverlayCommand.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.TypeParsers;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.Debugging;
|
||||
|
||||
[ToolshedCommand]
|
||||
internal sealed class OverlayCommand : ToolshedCommand
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _factory = default!;
|
||||
|
||||
[CommandImplementation("toggle")]
|
||||
internal void Toggle([CommandArgument(customParser:typeof(ReflectionTypeParser<Overlay>))] Type overlay)
|
||||
{
|
||||
if (!overlay.IsSubclassOf(typeof(Overlay)))
|
||||
throw new ArgumentException("Type must be a subclass of overlay");
|
||||
|
||||
if (_overlay.HasOverlay(overlay))
|
||||
Remove(overlay);
|
||||
else
|
||||
Add(overlay);
|
||||
}
|
||||
|
||||
[CommandImplementation("add")]
|
||||
internal void Add([CommandArgument(customParser: typeof(ReflectionTypeParser<Overlay>))] Type overlay)
|
||||
{
|
||||
if (!overlay.IsSubclassOf(typeof(Overlay)))
|
||||
throw new ArgumentException("Type must be a subclass of overlay");
|
||||
|
||||
if (!overlay.HasParameterlessConstructor())
|
||||
throw new ArgumentException("Type must have parameterless constructor");
|
||||
|
||||
if (_overlay.HasOverlay(overlay))
|
||||
return;
|
||||
|
||||
// TODO OVERLAYS Give overlays the ContentAccessAllowedAttribute?
|
||||
var instance = (Overlay) _factory.CreateInstanceUnchecked(overlay, oneOff: true);
|
||||
if (instance is IPostInjectInit init)
|
||||
init.PostInject();
|
||||
|
||||
_overlay.AddOverlay(instance);
|
||||
}
|
||||
|
||||
[CommandImplementation("remove")]
|
||||
public void Remove([CommandArgument(customParser: typeof(ReflectionTypeParser<Overlay>))] Type overlay)
|
||||
{
|
||||
_overlay.RemoveOverlay(overlay);
|
||||
}
|
||||
}
|
||||
229
Robust.Client/Debugging/Overlays/TileDebugOverlay.cs
Normal file
229
Robust.Client/Debugging/Overlays/TileDebugOverlay.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Debugging.Overlays;
|
||||
|
||||
/// <summary>
|
||||
/// This is an abstract helper class that can be used to create simple debug overlays that need to render tile based data.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public abstract class TileDebugOverlay : Overlay, IPostInjectInit
|
||||
{
|
||||
[Dependency] protected readonly IEntityManager Entity = default!;
|
||||
[Dependency] protected readonly IEyeManager Eye = default!;
|
||||
[Dependency] protected readonly IMapManager MapMan = default!;
|
||||
[Dependency] protected readonly IInputManager Input = default!;
|
||||
[Dependency] protected readonly IUserInterfaceManager Ui = default!;
|
||||
[Dependency] protected readonly IResourceCache Cache = default!;
|
||||
|
||||
protected SharedTransformSystem Transform = default!;
|
||||
protected MapSystem Map = default!;
|
||||
protected EntityLookupSystem Lookup = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace | OverlaySpace.ScreenSpace;
|
||||
|
||||
protected Font Font = default!;
|
||||
protected List<Entity<MapGridComponent>> Grids = new();
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
Transform = Entity.System<SharedTransformSystem>();
|
||||
Map = Entity.System<MapSystem>();
|
||||
Lookup = Entity.System<EntityLookupSystem>();
|
||||
var font = Cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf");
|
||||
Font = new VectorFont(font, 8);
|
||||
Init();
|
||||
}
|
||||
|
||||
protected virtual void Init()
|
||||
{
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
Grids.Clear();
|
||||
if (args.Viewport.Eye?.Position.MapId is not {} map || map == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
MapMan.FindGridsIntersecting(map, args.WorldBounds, ref Grids);
|
||||
|
||||
foreach (var grid in Grids)
|
||||
{
|
||||
switch (args.Space)
|
||||
{
|
||||
case OverlaySpace.ScreenSpace:
|
||||
DrawScreen(args, grid);
|
||||
break;
|
||||
case OverlaySpace.WorldSpace:
|
||||
DrawWorld(args, grid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Grids.Clear();
|
||||
}
|
||||
|
||||
protected virtual void DrawScreen(in OverlayDrawArgs args, Entity<MapGridComponent> grid)
|
||||
{
|
||||
var handle = args.ScreenHandle;
|
||||
var (_, _, matrix, invMatrix) = Transform.GetWorldPositionRotationMatrixWithInv(grid.Owner);
|
||||
var gridBounds = invMatrix.TransformBox(args.WorldBounds).Enlarged(grid.Comp.TileSize * 2);
|
||||
var tilesEnumerator = Map.GetLocalTilesEnumerator(grid, grid, gridBounds);
|
||||
while (tilesEnumerator.MoveNext(out var tile))
|
||||
{
|
||||
var tileBounds = Lookup.GetLocalBounds(tile, grid.Comp.TileSize);
|
||||
if (!gridBounds.Intersects(tileBounds))
|
||||
continue;
|
||||
var screenTileCentre = Eye.WorldToScreen(Vector2.Transform(tileBounds.Center, matrix));
|
||||
DrawTileText(handle, screenTileCentre, tile.GridIndices, grid);
|
||||
}
|
||||
|
||||
// Draw mouse tooltip
|
||||
DrawTooltip(handle);
|
||||
|
||||
}
|
||||
|
||||
protected virtual void DrawTooltip(DrawingHandleScreen handle)
|
||||
{
|
||||
var mousePos = Input.MouseScreenPosition;
|
||||
if (!mousePos.IsValid)
|
||||
return;
|
||||
|
||||
if (Ui.MouseGetControl(mousePos) is not IViewportControl viewport)
|
||||
return;
|
||||
|
||||
var coords = viewport.PixelToMap(mousePos.Position);
|
||||
|
||||
if (!MapMan.TryFindGridAt(coords, out var grid, out var comp))
|
||||
return;
|
||||
|
||||
var local = Map.WorldToLocal(grid, comp, coords.Position);
|
||||
var x = (int) Math.Floor(local.X / comp.TileSize);
|
||||
var y = (int) Math.Floor(local.Y / comp.TileSize);
|
||||
var indices = new Vector2i(x, y);
|
||||
|
||||
DrawTooltip(handle, mousePos.Position, local, indices, (grid, comp));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a tooltip around the mouse
|
||||
/// </summary>
|
||||
/// <param name="mouseScreen">The mouse's screen coordinates</param>
|
||||
/// <param name="mouseLocal">The mouse's local grid coordinates</param>
|
||||
/// <param name="indices">The mouse's tile indices</param>
|
||||
/// <param name="grid">The grid that the mouse is hovering over</param>
|
||||
protected virtual void DrawTooltip(DrawingHandleScreen handle, Vector2 mouseScreen, Vector2 mouseLocal, Vector2i indices, Entity<MapGridComponent> grid)
|
||||
{
|
||||
if (GetTooltip(mouseLocal, indices, grid) is not { } text)
|
||||
return;
|
||||
|
||||
var lineHeight = Font.GetLineHeight(1f);
|
||||
var offset = new Vector2(0, lineHeight);
|
||||
handle.DrawString(Font, mouseScreen - offset, text);
|
||||
}
|
||||
|
||||
protected virtual void DrawTileText(DrawingHandleScreen handle, Vector2 tileCentre, Vector2i indices, Entity<MapGridComponent> grid)
|
||||
{
|
||||
if (GetText(indices, grid) is {} text)
|
||||
handle.DrawString(Font, tileCentre, text);
|
||||
}
|
||||
|
||||
protected virtual void DrawWorld(in OverlayDrawArgs args, Entity<MapGridComponent> grid)
|
||||
{
|
||||
var handle = args.WorldHandle;
|
||||
var (_, _, matrix, invMatrix) = Transform.GetWorldPositionRotationMatrixWithInv(grid.Owner);
|
||||
var gridBounds = invMatrix.TransformBox(args.WorldBounds).Enlarged(grid.Comp.TileSize * 2);
|
||||
var tilesEnumerator = Map.GetLocalTilesEnumerator(grid, grid, gridBounds);
|
||||
while (tilesEnumerator.MoveNext(out var tile))
|
||||
{
|
||||
handle.SetTransform(matrix);
|
||||
var tileBounds = Lookup.GetLocalBounds(tile, grid.Comp.TileSize);
|
||||
if (gridBounds.Intersects(tileBounds))
|
||||
DrawTile(handle, tileBounds, tile.GridIndices, grid);
|
||||
}
|
||||
|
||||
handle.SetTransform(Matrix3x2.Identity);
|
||||
}
|
||||
|
||||
protected virtual void DrawTile(DrawingHandleWorld handle, Box2 tile, Vector2i indices, Entity<MapGridComponent> grid)
|
||||
{
|
||||
if (GetColor(indices, grid) is not { } color)
|
||||
return;
|
||||
|
||||
handle.DrawRect(tile, color.Border, filled: false);
|
||||
handle.DrawRect(tile, color.Fill, filled: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get text that will be rendered in a grid tile.
|
||||
/// </summary>
|
||||
protected abstract string? GetText(Vector2i indices, Entity<MapGridComponent> grid);
|
||||
|
||||
/// <summary>
|
||||
/// Get tooltip text that will be shown next to the mouse.
|
||||
/// </summary>
|
||||
/// <param name="mousePos">The mouse's position relative to the grid.</param>
|
||||
/// <param name="gridIndices">The grid indices corresponding to the mouse's position</param>
|
||||
/// <param name="grid">The grid that the mouse is over.</param>
|
||||
protected abstract string? GetTooltip(Vector2 mousePos, Vector2i indices, Entity<MapGridComponent> grid);
|
||||
|
||||
/// <summary>
|
||||
/// Get a border & fill color that will be used to draw a grid tile.
|
||||
/// </summary>
|
||||
protected abstract (Color Fill, Color Border)? GetColor(Vector2i indices, Entity<MapGridComponent> grid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="TileDebugOverlay"/> that exists to draw simple float information for each tile.
|
||||
/// </summary>
|
||||
public abstract class TileFloatDebugOverlay : TileDebugOverlay
|
||||
{
|
||||
protected virtual float MinValue => 0;
|
||||
protected virtual float MaxValue => 1;
|
||||
protected abstract float? GetData(Vector2i indices, Entity<MapGridComponent> grid);
|
||||
|
||||
protected override string? GetText(Vector2i indices, Entity<MapGridComponent> grid)
|
||||
{
|
||||
return GetData(indices, grid)?.ToString("F2");
|
||||
}
|
||||
|
||||
protected override string? GetTooltip(Vector2 mousePos, Vector2i indices, Entity<MapGridComponent> grid)
|
||||
{
|
||||
return GetData(indices, grid)?.ToString("F2");
|
||||
}
|
||||
|
||||
protected override (Color Fill, Color Border)? GetColor(Vector2i indices, Entity<MapGridComponent> grid)
|
||||
{
|
||||
if (GetData(indices, grid) is not { } value)
|
||||
return null;
|
||||
|
||||
var color = Gradient(value, MinValue, MaxValue);
|
||||
return (color.WithAlpha(0.2f), color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple yellow -> orange -> red gradient.
|
||||
/// </summary>
|
||||
public Color Gradient(float value, float min, float max)
|
||||
{
|
||||
// map min to 1, max to 0
|
||||
value = (value - min) / (max - min);
|
||||
return value < 0.5f
|
||||
? Color.InterpolateBetween(Color.Yellow, Color.Orange, value * 2)
|
||||
: Color.InterpolateBetween(Color.Orange, Color.Red, (value - 0.5f) * 2);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared;
|
||||
#if WINDOWS
|
||||
using TerraFX.Interop.Windows;
|
||||
using TerraFX.Interop.DirectX;
|
||||
#endif
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -17,8 +12,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private void InitGLContextManager()
|
||||
{
|
||||
CheckForceCompatMode();
|
||||
|
||||
// Advanced GL contexts currently disabled due to lack of testing etc.
|
||||
if (OperatingSystem.IsWindows() && _cfg.GetCVar(CVars.DisplayAngle))
|
||||
{
|
||||
@@ -61,74 +54,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_glContext = new GLContextWindow(this);
|
||||
}
|
||||
|
||||
private void CheckForceCompatMode()
|
||||
{
|
||||
#if WINDOWS
|
||||
// Qualcomm (Snapdragon/Adreno) devices have broken OpenGL drivers on Windows.
|
||||
|
||||
if (CheckIsQualcommDevice())
|
||||
{
|
||||
_sawmillOgl.Info("We appear to be on a Qualcomm device. Enabling compat mode due to broken OpenGL driver");
|
||||
_cfg.OverrideDefault(CVars.DisplayCompat, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
private static unsafe bool CheckIsQualcommDevice()
|
||||
{
|
||||
// Ideally we would check the OpenGL driver instead... but OpenGL is terrible so that's impossible.
|
||||
// Let's just check with DXGI instead.
|
||||
|
||||
IDXGIFactory1* dxgiFactory;
|
||||
ThrowIfFailed(
|
||||
nameof(DirectX.CreateDXGIFactory1),
|
||||
DirectX.CreateDXGIFactory1(Windows.__uuidof<IDXGIFactory1>(), (void**) &dxgiFactory));
|
||||
|
||||
try
|
||||
{
|
||||
uint idx = 0;
|
||||
IDXGIAdapter* adapter;
|
||||
while (dxgiFactory->EnumAdapters(idx, &adapter) != DXGI.DXGI_ERROR_NOT_FOUND)
|
||||
{
|
||||
try
|
||||
{
|
||||
DXGI_ADAPTER_DESC desc;
|
||||
ThrowIfFailed("GetDesc", adapter->GetDesc(&desc));
|
||||
|
||||
var descString = ((ReadOnlySpan<char>)desc.Description).TrimEnd('\0');
|
||||
if (descString.Contains("qualcomm", StringComparison.OrdinalIgnoreCase) ||
|
||||
descString.Contains("snapdragon", StringComparison.OrdinalIgnoreCase) ||
|
||||
descString.Contains("adreno", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
adapter->Release();
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
dxgiFactory->Release();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void ThrowIfFailed(string methodName, HRESULT hr)
|
||||
{
|
||||
if (Windows.FAILED(hr))
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private struct GLContextSpec
|
||||
{
|
||||
public int Major;
|
||||
|
||||
@@ -58,13 +58,11 @@ internal sealed class ReloadManager : IReloadManager
|
||||
{
|
||||
foreach (var file in _reloadQueue)
|
||||
{
|
||||
var rootedFile = file.ToRootedPath();
|
||||
|
||||
if (!_res.ContentFileExists(rootedFile))
|
||||
if (!_res.ContentFileExists(file))
|
||||
continue;
|
||||
|
||||
_sawmill.Info($"Reloading {rootedFile}");
|
||||
OnChanged?.Invoke(rootedFile);
|
||||
_sawmill.Info($"Reloading {file}");
|
||||
OnChanged?.Invoke(file);
|
||||
}
|
||||
|
||||
_reloadQueue.Clear();
|
||||
@@ -133,12 +131,13 @@ internal sealed class ReloadManager : IReloadManager
|
||||
var relPath = Path.GetRelativePath(rootIter, args.FullPath);
|
||||
if (relPath == args.FullPath)
|
||||
{
|
||||
// Not relative.
|
||||
// Different root (i.e., "C:/" and "D:/")
|
||||
continue;
|
||||
}
|
||||
|
||||
var file = ResPath.FromRelativeSystemPath(relPath);
|
||||
_reloadQueue.Add(file);
|
||||
var file = ResPath.FromRelativeSystemPath(relPath).ToRootedPath();
|
||||
if (!file.CanonPath.Contains("/../"))
|
||||
_reloadQueue.Add(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,4 +6,7 @@
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
|
||||
#if DEVELOPMENT
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting", AllInternalsVisible = true)]
|
||||
// The following allows another friend assembly access to the types marked as internal.
|
||||
// SS14 engine assemblies are friends.
|
||||
// This way internal is "Content can't touch this".
|
||||
[assembly: InternalsVisibleTo("Robust.Server")]
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.Lite")]
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("OpenToolkit.GraphicsLibraryFramework")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Gives access to Castle(Moq)
|
||||
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
|
||||
[assembly: InternalsVisibleTo("Robust.Client.WebView")]
|
||||
[assembly: InternalsVisibleTo("Robust.Packaging")]
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
[module: SkipLocalsInit]
|
||||
#endif
|
||||
|
||||
#if DEVELOPMENT
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
#endif
|
||||
|
||||
@@ -1204,7 +1204,7 @@ namespace Robust.Shared
|
||||
CVarDef.Create("display.use_US_QWERTY_hotkeys", false, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
public static readonly CVarDef<string> DisplayWindowingApi =
|
||||
CVarDef.Create("display.windowing_api", "glfw", CVar.CLIENTONLY);
|
||||
CVarDef.Create("display.windowing_api", "sdl3", CVar.CLIENTONLY);
|
||||
|
||||
/// <summary>
|
||||
/// If true and on Windows 11 Build 22000,
|
||||
|
||||
@@ -7,9 +7,11 @@ using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Collections;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.ComponentTrees;
|
||||
|
||||
@@ -19,7 +21,7 @@ namespace Robust.Shared.ComponentTrees;
|
||||
[UsedImplicitly]
|
||||
public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
where TTreeComp : Component, IComponentTreeComponent<TComp>, new()
|
||||
where TComp : Component, IComponentTreeEntry<TComp>, new()
|
||||
where TComp : Component, IComponentTreeEntry<TComp>
|
||||
{
|
||||
[Dependency] private readonly RecursiveMoveSystem _recursiveMoveSys = default!;
|
||||
[Dependency] protected readonly SharedTransformSystem XformSystem = default!;
|
||||
@@ -27,9 +29,17 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
|
||||
private readonly Queue<ComponentTreeEntry<TComp>> _updateQueue = new();
|
||||
private readonly HashSet<EntityUid> _updated = new();
|
||||
protected EntityQuery<TComp> Query;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this lookup tree should even be enabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can be used to disable some trees if they are not required, which helps improve performance a bit.
|
||||
/// </remarks>
|
||||
protected virtual bool Enabled => true;
|
||||
private bool _initialized;
|
||||
|
||||
/// <summary>
|
||||
/// If true, this system will update the tree positions every frame update. See also <see cref="DoTickUpdate"/>. Some systems may need to do both.
|
||||
/// </summary>
|
||||
@@ -55,6 +65,10 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
_initialized = true;
|
||||
UpdatesOutsidePrediction = DoTickUpdate;
|
||||
UpdatesAfter.Add(typeof(SharedTransformSystem));
|
||||
UpdatesAfter.Add(typeof(SharedPhysicsSystem));
|
||||
@@ -86,10 +100,21 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
_initialized = false;
|
||||
if (Recursive)
|
||||
{
|
||||
_recursiveMoveSys.OnTreeRecursiveMove -= HandleRecursiveMove;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckEnabled()
|
||||
{
|
||||
if (_initialized)
|
||||
return true;
|
||||
|
||||
Log.Error($"Attempted to use disabled lookup tree");
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Queue Update
|
||||
@@ -105,6 +130,9 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
|
||||
public void QueueTreeUpdate(EntityUid uid, TComp component, TransformComponent? xform = null)
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
if (component.TreeUpdateQueued || !Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
@@ -132,12 +160,10 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
|
||||
protected virtual void OnTreeRemove(EntityUid uid, TTreeComp component, ComponentRemove args)
|
||||
{
|
||||
if (Terminating(uid))
|
||||
return;
|
||||
|
||||
foreach (var entry in component.Tree)
|
||||
{
|
||||
entry.Component.TreeUid = null;
|
||||
entry.Component.Tree = null;
|
||||
}
|
||||
|
||||
component.Tree.Clear();
|
||||
@@ -145,6 +171,7 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
|
||||
protected virtual void OnTerminating(EntityUid uid, TTreeComp component, ref EntityTerminatingEvent args)
|
||||
{
|
||||
// IIRC, this is to prevent a tree-update spam as each of the entity's children get detached to nullspace.
|
||||
RemComp(uid, component);
|
||||
}
|
||||
|
||||
@@ -162,13 +189,13 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
#region Update Trees
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
if (DoTickUpdate)
|
||||
if (DoTickUpdate && _initialized)
|
||||
UpdateTreePositions();
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
if (DoFrameUpdate)
|
||||
if (DoFrameUpdate && _initialized)
|
||||
UpdateTreePositions();
|
||||
}
|
||||
|
||||
@@ -178,23 +205,25 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
/// </summary>
|
||||
public void UpdateTreePositions()
|
||||
{
|
||||
if (!CheckEnabled())
|
||||
return;
|
||||
|
||||
if (_updateQueue.Count == 0)
|
||||
return;
|
||||
|
||||
var xforms = GetEntityQuery<TransformComponent>();
|
||||
var trees = GetEntityQuery<TTreeComp>();
|
||||
|
||||
while (_updateQueue.TryDequeue(out var entry))
|
||||
{
|
||||
var (comp, xform) = entry;
|
||||
|
||||
// Was this entity queued multiple times?
|
||||
DebugTools.Assert(comp.TreeUpdateQueued, "Entity was queued multiple times?");
|
||||
|
||||
comp.TreeUpdateQueued = false;
|
||||
if (!comp.Running)
|
||||
continue;
|
||||
|
||||
if (!_updated.Add(entry.Uid))
|
||||
continue;
|
||||
|
||||
if (!comp.AddToTree || comp.Deleted || xform.MapUid == null)
|
||||
{
|
||||
RemoveFromTree(comp);
|
||||
@@ -211,8 +240,7 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
{
|
||||
(pos, rot) = XformSystem.GetRelativePositionRotation(
|
||||
entry.Transform,
|
||||
newTree!.Value,
|
||||
xforms);
|
||||
newTree!.Value);
|
||||
|
||||
newTreeComp!.Tree.Update(entry, ExtractAabb(entry, pos, rot));
|
||||
continue;
|
||||
@@ -228,13 +256,10 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
|
||||
(pos, rot) = XformSystem.GetRelativePositionRotation(
|
||||
entry.Transform,
|
||||
newTree!.Value,
|
||||
xforms);
|
||||
newTree!.Value);
|
||||
|
||||
newTreeComp.Tree.Add(entry, ExtractAabb(entry, pos, rot));
|
||||
}
|
||||
|
||||
_updated.Clear();
|
||||
}
|
||||
|
||||
private void RemoveFromTree(TComp component)
|
||||
@@ -253,8 +278,7 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
|
||||
var (pos, rot) = XformSystem.GetRelativePositionRotation(
|
||||
entry.Transform,
|
||||
entry.Component.TreeUid.Value,
|
||||
GetEntityQuery<TransformComponent>());
|
||||
entry.Component.TreeUid.Value);
|
||||
|
||||
return ExtractAabb(in entry, pos, rot);
|
||||
}
|
||||
@@ -268,6 +292,8 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
|
||||
public IEnumerable<(EntityUid Uid, TTreeComp Comp)> GetIntersectingTrees(MapId mapId, Box2 worldAABB)
|
||||
{
|
||||
if (!CheckEnabled())
|
||||
return [];
|
||||
// Anything that queries these trees should only do so if there are no queued updates, otherwise it can lead to
|
||||
// errors. Currently there is no easy way to enforce this, but this should work as long as nothing queries the
|
||||
// trees directly:
|
||||
@@ -299,26 +325,105 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
return state.trees;
|
||||
}
|
||||
|
||||
#region HashSet
|
||||
|
||||
public HashSet<ComponentTreeEntry<TComp>> QueryAabb(MapId mapId, Box2 worldBounds, bool approx = true)
|
||||
=> QueryAabb(mapId, new Box2Rotated(worldBounds, default, default), approx);
|
||||
|
||||
public void QueryAabb(HashSet<Entity<TComp, TransformComponent>> results, MapId mapId, Box2 worldBounds, bool approx = true)
|
||||
=> QueryAabb(results, mapId, new Box2Rotated(worldBounds, default, default), approx);
|
||||
|
||||
public HashSet<ComponentTreeEntry<TComp>> QueryAabb(MapId mapId, Box2Rotated worldBounds, bool approx = true)
|
||||
{
|
||||
var state = new HashSet<ComponentTreeEntry<TComp>>();
|
||||
QueryAabb(state, mapId, worldBounds, approx);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
[Obsolete("Use Entity<T> variant")]
|
||||
internal void QueryAabb(
|
||||
HashSet<ComponentTreeEntry<TComp>> results,
|
||||
MapId mapId,
|
||||
Box2Rotated worldBounds,
|
||||
bool approx = true)
|
||||
{
|
||||
if (!CheckEnabled())
|
||||
return;
|
||||
|
||||
foreach (var (tree, treeComp) in GetIntersectingTrees(mapId, worldBounds))
|
||||
{
|
||||
var bounds = XformSystem.GetInvWorldMatrix(tree).TransformBox(worldBounds);
|
||||
|
||||
treeComp.Tree.QueryAabb(ref state, static (ref HashSet<ComponentTreeEntry<TComp>> state, in ComponentTreeEntry<TComp> value) =>
|
||||
{
|
||||
state.Add(value);
|
||||
return true;
|
||||
},
|
||||
bounds, approx);
|
||||
treeComp.Tree.QueryAabb(ref results,
|
||||
static (ref HashSet<ComponentTreeEntry<TComp>> state, in ComponentTreeEntry<TComp> value) =>
|
||||
{
|
||||
state.Add(value);
|
||||
return true;
|
||||
},
|
||||
bounds,
|
||||
approx);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
public void QueryAabb(
|
||||
HashSet<Entity<TComp, TransformComponent>> results,
|
||||
MapId mapId,
|
||||
Box2Rotated worldBounds,
|
||||
bool approx = true)
|
||||
{
|
||||
if (!CheckEnabled())
|
||||
return;
|
||||
|
||||
foreach (var (tree, treeComp) in GetIntersectingTrees(mapId, worldBounds))
|
||||
{
|
||||
var bounds = XformSystem.GetInvWorldMatrix(tree).TransformBox(worldBounds);
|
||||
|
||||
treeComp.Tree.QueryAabb(ref results,
|
||||
static (ref HashSet<Entity<TComp, TransformComponent>> state, in ComponentTreeEntry<TComp> value) =>
|
||||
{
|
||||
state.Add(value);
|
||||
return true;
|
||||
},
|
||||
bounds,
|
||||
approx);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region List
|
||||
|
||||
public void QueryAabb(List<Entity<TComp, TransformComponent>> results, MapId mapId, Box2 worldBounds, bool approx = true)
|
||||
=> QueryAabb(results, mapId, new Box2Rotated(worldBounds, default, default), approx);
|
||||
|
||||
public void QueryAabb(
|
||||
List<Entity<TComp, TransformComponent>> results,
|
||||
MapId mapId,
|
||||
Box2Rotated worldBounds,
|
||||
bool approx = true)
|
||||
{
|
||||
if (!CheckEnabled())
|
||||
return;
|
||||
|
||||
foreach (var (tree, treeComp) in GetIntersectingTrees(mapId, worldBounds))
|
||||
{
|
||||
var bounds = XformSystem.GetInvWorldMatrix(tree).TransformBox(worldBounds);
|
||||
|
||||
treeComp.Tree.QueryAabb(ref results,
|
||||
static (ref List<Entity<TComp, TransformComponent>> state, in ComponentTreeEntry<TComp> value) =>
|
||||
{
|
||||
state.Add(value);
|
||||
return true;
|
||||
},
|
||||
bounds,
|
||||
approx);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void QueryAabb<TState>(
|
||||
ref TState state,
|
||||
DynamicTree<ComponentTreeEntry<TComp>>.QueryCallbackDelegate<TState> callback,
|
||||
@@ -336,6 +441,9 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
Box2Rotated worldBounds,
|
||||
bool approx = true)
|
||||
{
|
||||
if (!CheckEnabled())
|
||||
return;
|
||||
|
||||
foreach (var (tree, treeComp) in GetIntersectingTrees(mapId, worldBounds))
|
||||
{
|
||||
var bounds = XformSystem.GetInvWorldMatrix(tree).TransformBox(worldBounds);
|
||||
@@ -343,14 +451,92 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rays
|
||||
|
||||
[Obsolete("use IntersectRay")]
|
||||
public List<RayCastResults> IntersectRayWithPredicate<TState>(MapId mapId, in Ray ray, float maxLength,
|
||||
TState state, Func<EntityUid, TState, bool> predicate, bool returnOnFirstHit = true)
|
||||
{
|
||||
var list = new List<RayCastResults>();
|
||||
|
||||
if (!returnOnFirstHit)
|
||||
{
|
||||
IntersectRay(list, mapId, ray, maxLength, state, (e, s) => predicate(e.Owner, s));
|
||||
return list;
|
||||
}
|
||||
|
||||
var result = IntersectRay(mapId, ray, maxLength, state, (e, s) => predicate(e.Owner, s));
|
||||
if (result != null)
|
||||
list.Add(result.Value);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a ray intersection and return on the first hit.
|
||||
/// </summary>
|
||||
public RayCastResults? IntersectRay(MapId mapId, in Ray ray, float length)
|
||||
{
|
||||
var state = new QueryState(length);
|
||||
IntersectRayInternal(mapId, in ray, length, ref state, QueryCallback);
|
||||
return state.Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a ray intersection and populate a provided list of results.
|
||||
/// </summary>
|
||||
public void IntersectRay(List<RayCastResults> results, MapId mapId, in Ray ray, float maxLength)
|
||||
{
|
||||
results.Clear();
|
||||
var state = new QueryState(maxLength, results);
|
||||
IntersectRayInternal(mapId, in ray, maxLength, ref state, QueryCallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a ray intersection with a predicate and return on the first hit.
|
||||
/// </summary>
|
||||
public RayCastResults? IntersectRay<TState>(
|
||||
MapId mapId,
|
||||
in Ray ray,
|
||||
float length,
|
||||
TState predicateState,
|
||||
Func<Entity<TComp, TransformComponent>, TState, bool> ignore)
|
||||
{
|
||||
var state = new QueryState<TState>(new(length), predicateState, ignore);
|
||||
IntersectRayInternal(mapId, in ray, length, ref state, PredicateQueryCallback);
|
||||
return state.Inner.Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a ray intersection with a predicate and populate a provided list of results.
|
||||
/// </summary>
|
||||
public void IntersectRay<TState>(
|
||||
List<RayCastResults> results,
|
||||
MapId mapId,
|
||||
in Ray ray,
|
||||
float length,
|
||||
TState predicateState,
|
||||
Func<Entity<TComp, TransformComponent>, TState, bool> ignore)
|
||||
{
|
||||
var state = new QueryState<TState>(new(length, results), predicateState, ignore);
|
||||
IntersectRayInternal(mapId, in ray, length, ref state, PredicateQueryCallback);
|
||||
}
|
||||
|
||||
private void IntersectRayInternal<TState>(
|
||||
MapId mapId,
|
||||
in Ray ray,
|
||||
float maxLength,
|
||||
ref TState state,
|
||||
DynamicTree<ComponentTreeEntry<TComp>>.RayQueryCallbackDelegate<TState> callback)
|
||||
where TState : IDone
|
||||
{
|
||||
if (mapId == MapId.Nullspace)
|
||||
return new ();
|
||||
|
||||
var queryState = new QueryState<TState>(maxLength, returnOnFirstHit, state, predicate);
|
||||
return;
|
||||
|
||||
if (!CheckEnabled())
|
||||
return;
|
||||
var endPoint = ray.Position + ray.Direction * maxLength;
|
||||
var worldBox = new Box2(Vector2.Min(ray.Position, endPoint), Vector2.Max(ray.Position, endPoint));
|
||||
|
||||
@@ -359,43 +545,79 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
var (_, treeRot, matrix) = XformSystem.GetWorldPositionRotationInvMatrix(treeUid);
|
||||
var relativeAngle = new Angle(-treeRot.Theta).RotateVec(ray.Direction);
|
||||
var treeRay = new Ray(Vector2.Transform(ray.Position, matrix), relativeAngle);
|
||||
comp.Tree.QueryRay(ref queryState, QueryCallback, treeRay);
|
||||
if (returnOnFirstHit && queryState.List.Count > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return queryState.List;
|
||||
|
||||
static bool QueryCallback(
|
||||
ref QueryState<TState> state,
|
||||
in ComponentTreeEntry<TComp> value,
|
||||
in Vector2 point,
|
||||
float distFromOrigin)
|
||||
{
|
||||
if (distFromOrigin > state.MaxLength || state.Predicate.Invoke(value.Uid, state.State))
|
||||
return true;
|
||||
|
||||
state.List.Add(new RayCastResults(distFromOrigin, point, value.Uid));
|
||||
return !state.ReturnOnFirstHit;
|
||||
comp.Tree.QueryRay(ref state, callback, treeRay);
|
||||
if (state.Done)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct QueryState<TState>
|
||||
static bool QueryCallback(
|
||||
ref QueryState state,
|
||||
in ComponentTreeEntry<TComp> value,
|
||||
in Vector2 point,
|
||||
float dist)
|
||||
{
|
||||
public readonly float MaxLength;
|
||||
public readonly bool ReturnOnFirstHit;
|
||||
public readonly List<RayCastResults> List = new();
|
||||
public readonly TState State;
|
||||
public readonly Func<EntityUid, TState, bool> Predicate;
|
||||
if (dist > state.MaxLength)
|
||||
return true;
|
||||
|
||||
public QueryState(float maxLength, bool returnOnFirstHit, TState state, Func<EntityUid, TState, bool> predictate)
|
||||
if (state.ReturnOnFirstHit)
|
||||
{
|
||||
MaxLength = maxLength;
|
||||
ReturnOnFirstHit = returnOnFirstHit;
|
||||
State = state;
|
||||
Predicate = predictate;
|
||||
state.Result = new RayCastResults(dist, point, value.Uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
state.List.Add(new RayCastResults(dist, point, value.Uid));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool PredicateQueryCallback<TState>(
|
||||
ref QueryState<TState> state,
|
||||
in ComponentTreeEntry<TComp> value,
|
||||
in Vector2 point,
|
||||
float dist)
|
||||
{
|
||||
if (dist > state.Inner.MaxLength)
|
||||
return true;
|
||||
|
||||
if (state.Ignore.Invoke(value, state.PredicateState))
|
||||
return true;
|
||||
|
||||
if (state.Inner.ReturnOnFirstHit)
|
||||
{
|
||||
state.Inner.Result = new RayCastResults(dist, point, value.Uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
state.Inner.List.Add(new RayCastResults(dist, point, value.Uid));
|
||||
return true;
|
||||
}
|
||||
|
||||
private struct QueryState<TPredicateState>(
|
||||
QueryState inner,
|
||||
TPredicateState predicateState,
|
||||
Func<Entity<TComp, TransformComponent>, TPredicateState, bool> ignore) : IDone
|
||||
{
|
||||
public readonly TPredicateState PredicateState = predicateState;
|
||||
public readonly Func<Entity<TComp, TransformComponent>, TPredicateState, bool> Ignore = ignore;
|
||||
public QueryState Inner = inner;
|
||||
public bool Done => Inner.Done;
|
||||
}
|
||||
|
||||
private struct QueryState(float maxLength, List<RayCastResults>? list = null) : IDone
|
||||
{
|
||||
public readonly float MaxLength = maxLength;
|
||||
|
||||
[MemberNotNullWhen(false, nameof(List))]
|
||||
public readonly bool ReturnOnFirstHit => List == null;
|
||||
public readonly List<RayCastResults>? List = list;
|
||||
public RayCastResults? Result;
|
||||
public bool Done => Result != null;
|
||||
}
|
||||
|
||||
private interface IDone
|
||||
{
|
||||
bool Done { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public interface IComponentTreeEntry<TComp> where TComp : Component
|
||||
public DynamicTree<ComponentTreeEntry<TComp>>? Tree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the component should currently be added to a tree.
|
||||
/// Whether the component should currently be added to a tree.
|
||||
/// </summary>
|
||||
public bool AddToTree { get; }
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public abstract class LocalizedCommands : IConsoleCommand
|
||||
public virtual string Description => LocalizationManager.TryGetString($"cmd-{Command}-desc", out var val) ? val : "";
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Help => LocalizationManager.TryGetString($"cmd-{Command}-help", out var val) ? val : "";
|
||||
public virtual string Help => LocalizationManager.TryGetString($"cmd-{Command}-help", out var val, ("command", Command)) ? val : "";
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool RequireServerOrSingleplayer => false;
|
||||
|
||||
@@ -219,6 +219,9 @@ public abstract partial class SharedContainerSystem
|
||||
|
||||
internal void RecursivelyUpdateJoints(Entity<TransformComponent> entity)
|
||||
{
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (JointQuery.TryGetComponent(entity, out var jointComp))
|
||||
{
|
||||
// TODO: This is going to be going up while joints going down, although these aren't too common
|
||||
|
||||
@@ -95,10 +95,7 @@ public abstract partial class SharedContainerSystem
|
||||
_lookup.FindAndAddToEntityTree(toRemove, xform: xform);
|
||||
}
|
||||
|
||||
if (TryComp<JointComponent>(toRemove, out var jointComp))
|
||||
{
|
||||
_joint.RefreshRelay(toRemove, jointComp);
|
||||
}
|
||||
RecursivelyUpdateJoints((toRemove, xform));
|
||||
|
||||
// Raise container events (after re-parenting and internal remove).
|
||||
RaiseLocalEvent(container.Owner, new EntRemovedFromContainerMessage(toRemove, container), true);
|
||||
|
||||
@@ -88,6 +88,7 @@ namespace Robust.Shared.ContentPack
|
||||
public string SystemAssemblyName = default!;
|
||||
public HashSet<VerifierError> AllowedVerifierErrors = default!;
|
||||
public List<string> WhitelistedNamespaces = default!;
|
||||
public List<string> AllowedAssemblyPrefixes = default!;
|
||||
public Dictionary<string, Dictionary<string, TypeConfig>> Types = default!;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,16 @@ namespace Robust.Shared.ContentPack
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma warning disable RA0004
|
||||
var loadedConfig = _config.Result;
|
||||
#pragma warning restore RA0004
|
||||
|
||||
if (!loadedConfig.AllowedAssemblyPrefixes.Any(allowedNamePrefix => asmName.StartsWith(allowedNamePrefix)))
|
||||
{
|
||||
_sawmill.Error($"Assembly name '{asmName}' is not allowed for a content assembly");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VerifyIL)
|
||||
{
|
||||
if (!DoVerifyIL(asmName, resolver, peReader, reader))
|
||||
@@ -179,10 +189,6 @@ namespace Robust.Shared.ContentPack
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma warning disable RA0004
|
||||
var loadedConfig = _config.Result;
|
||||
#pragma warning restore RA0004
|
||||
|
||||
var badRefs = new ConcurrentBag<EntityHandle>();
|
||||
|
||||
// We still do explicit type reference scanning, even though the actual whitelists work with raw members.
|
||||
|
||||
@@ -93,19 +93,23 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
Sawmill.Debug("LOADING modules");
|
||||
var files = new Dictionary<string, (ResPath Path, string[] references)>();
|
||||
var files = new Dictionary<string, (ResPath Path, MemoryStream data, string[] references)>();
|
||||
|
||||
// Find all modules we want to load.
|
||||
foreach (var fullPath in paths)
|
||||
{
|
||||
using var asmFile = _res.ContentFileRead(fullPath);
|
||||
var refData = GetAssemblyReferenceData(asmFile);
|
||||
var ms = new MemoryStream();
|
||||
asmFile.CopyTo(ms);
|
||||
|
||||
ms.Position = 0;
|
||||
var refData = GetAssemblyReferenceData(ms);
|
||||
if (refData == null)
|
||||
continue;
|
||||
|
||||
var (asmRefs, asmName) = refData.Value;
|
||||
|
||||
if (!files.TryAdd(asmName, (fullPath, asmRefs)))
|
||||
if (!files.TryAdd(asmName, (fullPath, ms, asmRefs)))
|
||||
{
|
||||
Sawmill.Error("Found multiple modules with the same assembly name " +
|
||||
$"'{asmName}', A: {files[asmName].Path}, B: {fullPath}.");
|
||||
@@ -122,10 +126,10 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
Parallel.ForEach(files, pair =>
|
||||
{
|
||||
var (name, (path, _)) = pair;
|
||||
var (name, (_, data, _)) = pair;
|
||||
|
||||
using var stream = _res.ContentFileRead(path);
|
||||
if (!typeChecker.CheckAssembly(stream, resolver))
|
||||
data.Position = 0;
|
||||
if (!typeChecker.CheckAssembly(data, resolver))
|
||||
{
|
||||
throw new TypeCheckFailedException($"Assembly {name} failed type checks.");
|
||||
}
|
||||
@@ -137,14 +141,15 @@ namespace Robust.Shared.ContentPack
|
||||
var nodes = TopologicalSort.FromBeforeAfter(
|
||||
files,
|
||||
kv => kv.Key,
|
||||
kv => kv.Value.Path,
|
||||
kv => kv.Value,
|
||||
_ => Array.Empty<string>(),
|
||||
kv => kv.Value.references,
|
||||
allowMissing: true); // missing refs would be non-content assemblies so allow that.
|
||||
|
||||
// Actually load them in the order they depend on each other.
|
||||
foreach (var path in TopologicalSort.Sort(nodes))
|
||||
foreach (var item in TopologicalSort.Sort(nodes))
|
||||
{
|
||||
var (path, memory, _) = item;
|
||||
Sawmill.Debug($"Loading module: '{path}'");
|
||||
try
|
||||
{
|
||||
@@ -156,9 +161,9 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
else
|
||||
{
|
||||
using var assemblyStream = _res.ContentFileRead(path);
|
||||
memory.Position = 0;
|
||||
using var symbolsStream = _res.ContentFileReadOrNull(path.WithExtension("pdb"));
|
||||
LoadGameAssembly(assemblyStream, symbolsStream, skipVerify: true);
|
||||
LoadGameAssembly(memory, symbolsStream, skipVerify: true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -174,7 +179,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
private (string[] refs, string name)? GetAssemblyReferenceData(Stream stream)
|
||||
{
|
||||
using var reader = ModLoader.MakePEReader(stream);
|
||||
using var reader = ModLoader.MakePEReader(stream, leaveOpen: true);
|
||||
var metaReader = reader.GetMetadataReader();
|
||||
|
||||
var name = metaReader.GetString(metaReader.GetAssemblyDefinition().Name);
|
||||
|
||||
@@ -68,7 +68,11 @@ namespace Robust.Shared.ContentPack
|
||||
internal static string SafeGetResourcePath(string baseDir, ResPath path)
|
||||
{
|
||||
var relSysPath = path.ToRelativeSystemPath();
|
||||
if (relSysPath.Contains("\\..") || relSysPath.Contains("/.."))
|
||||
|
||||
// This also blocks files like "..foo.yml". But whatever, I CBF fixing that.
|
||||
if (relSysPath.Contains("\\..")
|
||||
|| relSysPath.Contains("/..")
|
||||
|| relSysPath.StartsWith(".."))
|
||||
{
|
||||
// Hard cap on any exploit smuggling a .. in there.
|
||||
// Since that could allow leaving sandbox.
|
||||
|
||||
@@ -17,6 +17,10 @@ WhitelistedNamespaces:
|
||||
- Content
|
||||
- OpenDreamShared
|
||||
|
||||
AllowedAssemblyPrefixes:
|
||||
- OpenDream
|
||||
- Content
|
||||
|
||||
# The type whitelist does NOT care about which assembly types come from.
|
||||
# This is because types switch assembly all the time.
|
||||
# Just look up stuff like StreamReader on https://apisof.net.
|
||||
@@ -1089,6 +1093,7 @@ Types:
|
||||
- "string ToHexString(System.ReadOnlySpan`1<byte>)"
|
||||
|
||||
Converter`2: { All: True } # Delegate
|
||||
DateOnly: { All: True }
|
||||
DateTime: { All: True }
|
||||
DateTimeKind: { } # Enum
|
||||
DateTimeOffset: { All: True }
|
||||
@@ -1421,6 +1426,7 @@ Types:
|
||||
- "void CopyTo(int, char[], int, int)"
|
||||
StringComparison: { } # Enum
|
||||
StringSplitOptions: { } # Enum
|
||||
TimeOnly: { All: True }
|
||||
TimeSpan: { All: True }
|
||||
Type:
|
||||
# COM, marshalling, interop, etc... stuff omitted.
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.EntitySerialization.Components;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -33,7 +30,8 @@ namespace Robust.Shared.EntitySerialization;
|
||||
public sealed class EntityDeserializer :
|
||||
ISerializationContext,
|
||||
ITypeSerializer<EntityUid, ValueDataNode>,
|
||||
ITypeSerializer<NetEntity, ValueDataNode>
|
||||
ITypeSerializer<NetEntity, ValueDataNode>,
|
||||
ITypeSerializer<MapId, ValueDataNode>
|
||||
{
|
||||
// See the comments around EntitySerializer's version const for information about the different versions.
|
||||
// TBH version three isn't even really fully supported anymore, simply due to changes in engine component serialization.
|
||||
@@ -93,6 +91,7 @@ public sealed class EntityDeserializer :
|
||||
public readonly LoadResult Result = new();
|
||||
public readonly Dictionary<int, string> TileMap = new();
|
||||
public readonly Dictionary<int, EntityUid> UidMap = new();
|
||||
public readonly Dictionary<int, MapId> AllocatedMapIds = new();
|
||||
public readonly List<int> MapYamlIds = new();
|
||||
public readonly List<int> GridYamlIds = new();
|
||||
public readonly List<int> OrphanYamlIds = new();
|
||||
@@ -185,6 +184,10 @@ public sealed class EntityDeserializer :
|
||||
// Alloc entities, and populate the yaml uid -> EntityUid maps
|
||||
AllocateEntities();
|
||||
|
||||
// Assign a map id to each map entity. This is required to de-serialize map coordinates & map ids
|
||||
if (Options.AssignMapIds)
|
||||
AllocateMapIds();
|
||||
|
||||
// Load the prototype data onto entities, e.g. transform parents, etc.
|
||||
LoadEntities();
|
||||
|
||||
@@ -197,7 +200,7 @@ public sealed class EntityDeserializer :
|
||||
// Assign MapSaveTileMapComponent to all read grids. This is used to avoid large file diffs if the tile map changes.
|
||||
StoreGridTileMap();
|
||||
|
||||
if (Options.AssignMapids)
|
||||
if (Options.AssignMapIds)
|
||||
AssignMapIds();
|
||||
|
||||
CheckCategory();
|
||||
@@ -224,6 +227,15 @@ public sealed class EntityDeserializer :
|
||||
InitializeMaps();
|
||||
|
||||
ProcessDeletions();
|
||||
|
||||
if (!Options.AssignMapIds)
|
||||
return;
|
||||
|
||||
foreach (var yamlId in MapYamlIds)
|
||||
{
|
||||
if (AllocatedMapIds.TryGetValue(yamlId, out var alloc) && EntMan.EntityExists(UidMap[yamlId]))
|
||||
DebugTools.AssertEqual(_map.GetMap(alloc), UidMap[yamlId]);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadMetadata()
|
||||
@@ -520,6 +532,18 @@ public sealed class EntityDeserializer :
|
||||
_log.Debug($"Allocated {Entities.Count} entities in {_stopwatch.Elapsed}");
|
||||
}
|
||||
|
||||
private void AllocateMapIds()
|
||||
{
|
||||
if (Result.Version < 7)
|
||||
return; // MapYamlIds is not populated untill later in older versions
|
||||
|
||||
foreach (var yamlMapId in MapYamlIds)
|
||||
{
|
||||
var mapUid = UidMap[yamlMapId];
|
||||
AllocatedMapIds[yamlMapId] = _map.AllocateMapId(mapUid);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadMapsAndGrids()
|
||||
{
|
||||
if (Result.Version < 7)
|
||||
@@ -1222,5 +1246,48 @@ public sealed class EntityDeserializer :
|
||||
: new ValueDataNode("invalid");
|
||||
}
|
||||
|
||||
ValidationNode ITypeValidator<MapId, ValueDataNode>.Validate(
|
||||
ISerializationManager seri,
|
||||
ValueDataNode node,
|
||||
IDependencyCollection deps,
|
||||
ISerializationContext? context)
|
||||
{
|
||||
return seri.ValidateNode<EntityUid>(node, context);
|
||||
}
|
||||
|
||||
MapId ITypeReader<MapId, ValueDataNode>.Read(
|
||||
ISerializationManager seri,
|
||||
ValueDataNode node,
|
||||
IDependencyCollection deps,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? ctx,
|
||||
ISerializationManager.InstantiationDelegate<MapId>? instanceProvider)
|
||||
{
|
||||
if (!Options.AssignMapIds || Result.Version < 7)
|
||||
{
|
||||
_log.Error("Cannot deserialize map ids without pre-allocated ids");
|
||||
return MapId.Nullspace;
|
||||
}
|
||||
|
||||
if (int.TryParse(node.Value, out var val) && AllocatedMapIds.TryGetValue(val, out var map))
|
||||
return map;
|
||||
|
||||
var msg = CurrentReadingEntity is not { } ent
|
||||
? "Encountered unknown yaml map id"
|
||||
: $"Encountered unknown yaml map id wile reading entity {ent.YamlId}, component: {CurrentComponent}";
|
||||
_log.Error(msg);
|
||||
return MapId.Nullspace;
|
||||
}
|
||||
|
||||
DataNode ITypeWriter<MapId>.Write(
|
||||
ISerializationManager seri,
|
||||
MapId value,
|
||||
IDependencyCollection deps,
|
||||
bool alwaysWrite,
|
||||
ISerializationContext? ctx)
|
||||
{
|
||||
return seri.WriteValue(_map.GetMapOrInvalid(value), alwaysWrite, ctx);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -38,7 +38,8 @@ namespace Robust.Shared.EntitySerialization;
|
||||
/// </remarks>
|
||||
public sealed class EntitySerializer : ISerializationContext,
|
||||
ITypeSerializer<EntityUid, ValueDataNode>,
|
||||
ITypeSerializer<NetEntity, ValueDataNode>
|
||||
ITypeSerializer<NetEntity, ValueDataNode>,
|
||||
ITypeSerializer<MapId, ValueDataNode>
|
||||
{
|
||||
public const int MapFormatVersion = 7;
|
||||
// v6->v7: PR #5572 - Added more metadata, List maps/grids/orphans, include some life-stage information
|
||||
@@ -56,12 +57,12 @@ public sealed class EntitySerializer : ISerializationContext,
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDef = default!;
|
||||
[Dependency] private readonly IConfigurationManager _conf = default!;
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
|
||||
private readonly ISawmill _log;
|
||||
public readonly Dictionary<EntityUid, int> YamlUidMap = new();
|
||||
public readonly HashSet<int> YamlIds = new();
|
||||
|
||||
|
||||
public string? CurrentComponent { get; private set; }
|
||||
public Entity<MetaDataComponent>? CurrentEntity { get; private set; }
|
||||
public int CurrentEntityYamlUid { get; private set; }
|
||||
@@ -252,6 +253,42 @@ public sealed class EntitySerializer : ISerializationContext,
|
||||
Truncate = EntityUid.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes several entities and all of their children. Note that this will not automatically serialize the
|
||||
/// entity's parents.
|
||||
/// </summary>
|
||||
public void SerializeEntityRecursive(HashSet<EntityUid> roots)
|
||||
{
|
||||
if (roots.Count == 0)
|
||||
return;
|
||||
|
||||
InitializeTileMap(roots.First());
|
||||
|
||||
HashSet<EntityUid> allEntities = new();
|
||||
List<(EntityUid Root, HashSet<EntityUid> Children)> entities = new();
|
||||
|
||||
foreach(var root in roots)
|
||||
{
|
||||
if (!IsSerializable(root))
|
||||
throw new Exception($"{EntMan.ToPrettyString(root)} is not serializable");
|
||||
|
||||
var ents = new HashSet<EntityUid>();
|
||||
RecursivelyIncludeChildren(root, ents);
|
||||
entities.Add((root, ents));
|
||||
allEntities.UnionWith(ents);
|
||||
}
|
||||
|
||||
ReserveYamlIds(allEntities);
|
||||
|
||||
foreach (var (root, children) in entities)
|
||||
{
|
||||
Truncate = _xformQuery.GetComponent(root).ParentUid;
|
||||
Truncated.Add(Truncate);
|
||||
SerializeEntitiesInternal(children);
|
||||
Truncate = EntityUid.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -939,7 +976,6 @@ public sealed class EntitySerializer : ISerializationContext,
|
||||
if (YamlUidMap.TryGetValue(value, out var yamlId))
|
||||
return new ValueDataNode(yamlId.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
|
||||
if (CurrentComponent == _xformName)
|
||||
{
|
||||
if (value == EntityUid.Invalid)
|
||||
@@ -1052,5 +1088,41 @@ public sealed class EntitySerializer : ISerializationContext,
|
||||
return serializationManager.WriteValue(uid, alwaysWrite, context);
|
||||
}
|
||||
|
||||
ValidationNode ITypeValidator<MapId, ValueDataNode>.Validate(
|
||||
ISerializationManager seri,
|
||||
ValueDataNode node,
|
||||
IDependencyCollection deps,
|
||||
ISerializationContext? context)
|
||||
{
|
||||
return seri.ValidateNode<EntityUid>(node, context);
|
||||
}
|
||||
|
||||
MapId ITypeReader<MapId, ValueDataNode>.Read(
|
||||
ISerializationManager seri,
|
||||
ValueDataNode node,
|
||||
IDependencyCollection deps,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? ctx,
|
||||
ISerializationManager.InstantiationDelegate<MapId>? instanceProvider)
|
||||
{
|
||||
return EntMan.TryGetComponent(seri.Read<EntityUid>(node, ctx), out MapComponent? mapComp)
|
||||
? mapComp.MapId
|
||||
: MapId.Nullspace;
|
||||
}
|
||||
|
||||
DataNode ITypeWriter<MapId>.Write(
|
||||
ISerializationManager seri,
|
||||
MapId value,
|
||||
IDependencyCollection deps,
|
||||
bool alwaysWrite,
|
||||
ISerializationContext? ctx)
|
||||
{
|
||||
if (_map.TryGetMap(value, out var uid))
|
||||
return seri.WriteValue(uid, alwaysWrite, ctx);
|
||||
|
||||
_log.Error($"Attempted to serialize invalid map id {value} while serializing component '{CurrentComponent}' on entity '{EntMan.ToPrettyString(uid)}'");
|
||||
return new ValueDataNode("invalid");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -83,10 +83,10 @@ public record struct DeserializationOptions()
|
||||
public bool LogInvalidEntities = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to automatically assign map ids to any deserialized map entities.
|
||||
/// Whether to automatically assign map ids to any deserialized map entities.
|
||||
/// If false, maps need to be manually given ids before entities are initialized.
|
||||
/// </summary>
|
||||
public bool AssignMapids = true;
|
||||
public bool AssignMapIds = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,13 +4,11 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -97,7 +95,7 @@ public sealed partial class MapLoaderSystem
|
||||
var opts = options ?? MapLoadOptions.Default;
|
||||
|
||||
// If we are forcing a map id, we cannot auto-assign ids.
|
||||
opts.DeserializationOptions.AssignMapids = opts.ForceMapId == null;
|
||||
opts.DeserializationOptions.AssignMapIds = opts.ForceMapId == null;
|
||||
|
||||
if (opts.MergeMap is { } targetId && !_mapSystem.MapExists(targetId))
|
||||
throw new Exception($"Target map {targetId} does not exist");
|
||||
|
||||
@@ -16,7 +16,7 @@ public sealed partial class MapLoaderSystem
|
||||
public event EntitySerializer.IsSerializableDelegate? OnIsSerializable;
|
||||
|
||||
/// <summary>
|
||||
/// Recursively serialize the given entity and its children.
|
||||
/// Recursively serialize the given entities and all of their children.
|
||||
/// </summary>
|
||||
public (MappingDataNode Node, FileCategory Category) SerializeEntitiesRecursive(
|
||||
HashSet<EntityUid> entities,
|
||||
@@ -41,12 +41,7 @@ public sealed partial class MapLoaderSystem
|
||||
|
||||
var serializer = new EntitySerializer(_dependency, opts);
|
||||
serializer.OnIsSerializeable += OnIsSerializable;
|
||||
|
||||
foreach (var ent in entities)
|
||||
{
|
||||
serializer.SerializeEntityRecursive(ent);
|
||||
}
|
||||
|
||||
serializer.SerializeEntityRecursive(entities);
|
||||
var data = serializer.Write();
|
||||
var cat = serializer.GetCategory();
|
||||
|
||||
|
||||
@@ -352,7 +352,9 @@ namespace Robust.Shared.GameObjects
|
||||
throw new ArgumentException($"Attempted to spawn entity on an invalid map. Coordinates: {coordinates}");
|
||||
|
||||
EntityCoordinates coords;
|
||||
if (transform.Anchored && _mapManager.TryFindGridAt(coordinates, out var gridUid, out var grid))
|
||||
if (_mapManager.TryFindGridAt(coordinates, out var gridUid, out var grid)
|
||||
&& MetaQuery.TryGetComponentInternal(gridUid, out var meta)
|
||||
&& meta.EntityLifeStage < EntityLifeStage.Terminating)
|
||||
{
|
||||
coords = new EntityCoordinates(gridUid, _mapSystem.WorldToLocal(gridUid, grid, coordinates.Position));
|
||||
_xforms.SetCoordinates(newEntity, transform, coords, rotation, unanchor: false);
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="prototypeName">Name of the <see cref="EntityPrototype"/> to spawn.</param>
|
||||
/// <param name="coordinates">Coordinates to place the newly spawned entity.</param>
|
||||
/// <param name="overrides">Overrides to add or remove components that differ from the prototype.</param>
|
||||
/// <param name="rotation">Map rotation to set the newly spawned entity to.</param>
|
||||
/// <param name="rotation">Local rotation to set the newly spawned entity to.</param>
|
||||
/// <returns>A new uninitialized entity.</returns>
|
||||
/// <remarks>If there is a grid at the <paramref name="coordinates"/>, the entity will be parented to the grid.
|
||||
/// Otherwise, it will be parented to the map.</remarks>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.ComponentTrees;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -8,6 +10,8 @@ using Robust.Shared.Utility;
|
||||
namespace Robust.Shared.GameObjects;
|
||||
public abstract class OccluderSystem : ComponentTreeSystem<OccluderTreeComponent, OccluderComponent>
|
||||
{
|
||||
public const float MaxRaycastRange = 100f;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -69,4 +73,84 @@ public abstract class OccluderSystem : ComponentTreeSystem<OccluderTreeComponent
|
||||
QueueTreeUpdate(uid, comp);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region InRangeUnoccluded
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if two points are within the specified range and there are no occluders between them that aren't
|
||||
/// ignored by the predicate.
|
||||
/// </summary>
|
||||
public bool InRangeUnoccluded<TState>(
|
||||
MapCoordinates origin,
|
||||
MapCoordinates other,
|
||||
float range,
|
||||
TState state,
|
||||
Func<Entity<OccluderComponent, TransformComponent>, TState, bool> ignore)
|
||||
{
|
||||
if (!GetRay(origin, other, range, out var length, out var ray, out var result))
|
||||
return result;
|
||||
|
||||
return IntersectRay(origin.MapId, ray, length, state, ignore) == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if two points are within the specified range and there are no occluders between them.
|
||||
/// </summary>
|
||||
/// <param name="ignoreTouching">If true, this will use <see cref="IsTouchingEndpoint"/> as a predicate to ignore \
|
||||
/// occluders that are touching the start or end point.</param>
|
||||
public bool InRangeUnoccluded(MapCoordinates origin, MapCoordinates other, float range, bool ignoreTouching)
|
||||
{
|
||||
if (!GetRay(origin, other, range, out var length, out var ray, out var result))
|
||||
return result;
|
||||
|
||||
if (!ignoreTouching)
|
||||
return IntersectRay(origin.MapId, ray, length) == null;
|
||||
|
||||
var state = (XformSystem, origin.Position, other.Position);
|
||||
return IntersectRay(origin.MapId, ray, length, state, IsTouchingEndpoint) == null;
|
||||
}
|
||||
|
||||
private bool GetRay(MapCoordinates origin, MapCoordinates other, float range, out float length, out Ray ray, out bool result)
|
||||
{
|
||||
ray = default;
|
||||
length = default;
|
||||
result = false;
|
||||
if (other.MapId != origin.MapId || other.MapId == MapId.Nullspace)
|
||||
return false;
|
||||
|
||||
var dir = other.Position - origin.Position;
|
||||
length = dir.Length();
|
||||
if (MathHelper.CloseTo(length, 0))
|
||||
{
|
||||
result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
var normalized = dir / length;
|
||||
|
||||
if (range > 0f && length > range + 0.01f)
|
||||
return false;
|
||||
|
||||
if (length > MaxRaycastRange)
|
||||
{
|
||||
Log.Warning($"{nameof(InRangeUnoccluded)} check performed over extreme range. Limiting range.");
|
||||
length = MaxRaycastRange;
|
||||
}
|
||||
|
||||
ray = new Ray(origin.Position, normalized);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple predicate for use with <see cref="InRangeUnoccluded"/> that will ignore any occluders that intersect the
|
||||
/// start and end points.
|
||||
/// </summary>
|
||||
public static bool IsTouchingEndpoint(Entity<OccluderComponent, TransformComponent> ent, (SharedTransformSystem Sys, Vector2 Start, Vector2 End) state)
|
||||
{
|
||||
var occluderBox = ent.Comp1.BoundingBox;
|
||||
occluderBox = occluderBox.Translated(state.Sys.GetWorldPosition(ent.Comp2));
|
||||
return occluderBox.Contains(state.Start) || occluderBox.Contains(state.End);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Robust.Shared.GameObjects;
|
||||
public abstract partial class SharedMapSystem
|
||||
{
|
||||
protected int LastMapId;
|
||||
private Dictionary<EntityUid, MapId> _reserved = new();
|
||||
|
||||
private void InitializeMap()
|
||||
{
|
||||
@@ -128,6 +129,17 @@ public abstract partial class SharedMapSystem
|
||||
EnsureComp<GridTreeComponent>(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate & reserve a map-id for a map-entity before it is actually given the component.
|
||||
/// </summary>
|
||||
internal MapId AllocateMapId(EntityUid ent)
|
||||
{
|
||||
var id = _reserved[ent] = TakeNextMapId();
|
||||
Maps.Add(id, ent);
|
||||
UsedIds.Add(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
internal void AssignMapId(Entity<MapComponent> map, MapId? id = null)
|
||||
{
|
||||
if (map.Comp.MapId != MapId.Nullspace)
|
||||
@@ -148,6 +160,15 @@ public abstract partial class SharedMapSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (_reserved.TryGetValue(map.Owner, out var reserved))
|
||||
{
|
||||
DebugTools.AssertNull(id);
|
||||
DebugTools.AssertEqual(Maps[reserved], map.Owner);
|
||||
DebugTools.Assert(UsedIds.Contains(reserved));
|
||||
map.Comp.MapId = reserved;
|
||||
return;
|
||||
}
|
||||
|
||||
map.Comp.MapId = id ?? TakeNextMapId();
|
||||
|
||||
if (IsClientSide(map) != map.Comp.MapId.IsClientSide)
|
||||
|
||||
@@ -1028,6 +1028,15 @@ public abstract partial class SharedTransformSystem
|
||||
return GetWorldPositionRotation(component);
|
||||
}
|
||||
|
||||
[Obsolete("Use variant without entity query")]
|
||||
public (Vector2 Position, Angle Rotation) GetRelativePositionRotation(
|
||||
TransformComponent component,
|
||||
EntityUid relative,
|
||||
EntityQuery<TransformComponent> query)
|
||||
{
|
||||
return GetRelativePositionRotation(component, relative);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the position and rotation relative to some entity higher up in the component's transform hierarchy.
|
||||
/// </summary>
|
||||
@@ -1035,15 +1044,14 @@ public abstract partial class SharedTransformSystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public (Vector2 Position, Angle Rotation) GetRelativePositionRotation(
|
||||
TransformComponent component,
|
||||
EntityUid relative,
|
||||
EntityQuery<TransformComponent> query)
|
||||
EntityUid relative)
|
||||
{
|
||||
var rot = component._localRotation;
|
||||
var pos = component._localPosition;
|
||||
var xform = component;
|
||||
while (xform.ParentUid != relative)
|
||||
{
|
||||
if (xform.ParentUid.IsValid() && query.TryGetComponent(xform.ParentUid, out xform))
|
||||
if (xform.ParentUid.IsValid() && TryComp(xform.ParentUid, out xform))
|
||||
{
|
||||
rot += xform._localRotation;
|
||||
pos = xform._localRotation.RotateVec(pos) + xform._localPosition;
|
||||
@@ -1052,30 +1060,36 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
// Entity was not actually in the transform hierarchy. This is probably a sign that something is wrong, or that the function is being misused.
|
||||
Log.Warning($"Target entity ({ToPrettyString(relative)}) not in transform hierarchy while calling {nameof(GetRelativePositionRotation)}.");
|
||||
var relXform = query.GetComponent(relative);
|
||||
var relXform = Transform(relative);
|
||||
pos = Vector2.Transform(pos, GetInvWorldMatrix(relXform));
|
||||
rot = rot - GetWorldRotation(relXform, query);
|
||||
rot = rot - GetWorldRotation(relXform);
|
||||
break;
|
||||
}
|
||||
|
||||
return (pos, rot);
|
||||
}
|
||||
|
||||
[Obsolete("Use variant without entity query")]
|
||||
public Vector2 GetRelativePosition(
|
||||
TransformComponent component,
|
||||
EntityUid relative,
|
||||
EntityQuery<TransformComponent> query)
|
||||
{
|
||||
return GetRelativePosition(component, relative);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the position and rotation relative to some entity higher up in the component's transform hierarchy.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vector2 GetRelativePosition(
|
||||
TransformComponent component,
|
||||
EntityUid relative,
|
||||
EntityQuery<TransformComponent> query)
|
||||
public Vector2 GetRelativePosition(TransformComponent component, EntityUid relative)
|
||||
{
|
||||
var pos = component._localPosition;
|
||||
var xform = component;
|
||||
while (xform.ParentUid != relative)
|
||||
{
|
||||
if (xform.ParentUid.IsValid() && query.TryGetComponent(xform.ParentUid, out xform))
|
||||
if (xform.ParentUid.IsValid() && TryComp(xform.ParentUid, out xform))
|
||||
{
|
||||
pos = xform._localRotation.RotateVec(pos) + xform._localPosition;
|
||||
continue;
|
||||
@@ -1083,7 +1097,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
// Entity was not actually in the transform hierarchy. This is probably a sign that something is wrong, or that the function is being misused.
|
||||
Log.Warning($"Target entity ({ToPrettyString(relative)}) not in transform hierarchy while calling {nameof(GetRelativePositionRotation)}.");
|
||||
var relXform = query.GetComponent(relative);
|
||||
var relXform = Transform(relative);
|
||||
pos = Vector2.Transform(pos, GetInvWorldMatrix(relXform));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Map
|
||||
@@ -12,8 +13,8 @@ namespace Robust.Shared.Map
|
||||
/// <summary>
|
||||
/// A set of coordinates relative to another entity.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public readonly struct EntityCoordinates : IEquatable<EntityCoordinates>, ISpanFormattable
|
||||
[PublicAPI, DataRecord]
|
||||
public readonly record struct EntityCoordinates : ISpanFormattable
|
||||
{
|
||||
public static readonly EntityCoordinates Invalid = new(EntityUid.Invalid, Vector2.Zero);
|
||||
|
||||
@@ -313,44 +314,6 @@ namespace Robust.Shared.Map
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IEquatable
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(EntityCoordinates other)
|
||||
{
|
||||
return EntityId.Equals(other.EntityId) && Position.Equals(other.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is EntityCoordinates other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(EntityId, Position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for equality by value between two objects.
|
||||
/// </summary>
|
||||
public static bool operator ==(EntityCoordinates left, EntityCoordinates right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for inequality by value between two objects.
|
||||
/// </summary>
|
||||
public static bool operator !=(EntityCoordinates left, EntityCoordinates right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Map
|
||||
@@ -10,9 +11,9 @@ namespace Robust.Shared.Map
|
||||
/// <summary>
|
||||
/// Coordinates relative to a specific map.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[PublicAPI, DataRecord]
|
||||
[Serializable, NetSerializable]
|
||||
public readonly struct MapCoordinates : IEquatable<MapCoordinates>, ISpanFormattable
|
||||
public readonly record struct MapCoordinates : ISpanFormattable
|
||||
{
|
||||
public static readonly MapCoordinates Nullspace = new(Vector2.Zero, MapId.Nullspace);
|
||||
|
||||
@@ -95,46 +96,6 @@ namespace Robust.Shared.Map
|
||||
return (otherCoords.Position - Position).LengthSquared() < range * range;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(MapCoordinates other)
|
||||
{
|
||||
return Position.Equals(other.Position) && MapId.Equals(other.MapId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
return false;
|
||||
return obj is MapCoordinates other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Position.GetHashCode() * 397) ^ MapId.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for equality by value between two objects.
|
||||
/// </summary>
|
||||
public static bool operator ==(MapCoordinates a, MapCoordinates b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for inequality by value between two objects.
|
||||
/// </summary>
|
||||
public static bool operator !=(MapCoordinates a, MapCoordinates b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used to deconstruct this object into a tuple.
|
||||
/// </summary>
|
||||
|
||||
@@ -31,6 +31,11 @@ public readonly struct ComponentTreeEntry<T> : IEquatable<ComponentTreeEntry<T>>
|
||||
xform = Transform;
|
||||
}
|
||||
|
||||
public static implicit operator Entity<T, TransformComponent>(ComponentTreeEntry<T> entry)
|
||||
{
|
||||
return new(entry.Uid, entry.Component, entry.Transform);
|
||||
}
|
||||
|
||||
public static implicit operator ComponentTreeEntry<T>((T, TransformComponent) tuple)
|
||||
{
|
||||
return new ComponentTreeEntry<T>()
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// The following allows another friend assembly access to the types marked as internal.
|
||||
// SS14 engine assemblies are friends.
|
||||
// This way internal is "Content can't touch this".
|
||||
[assembly: InternalsVisibleTo("Robust.Server")]
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.Lite")]
|
||||
[assembly: InternalsVisibleTo("Robust.UnitTesting")]
|
||||
[assembly: InternalsVisibleTo("OpenToolkit.GraphicsLibraryFramework")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Gives access to Castle(Moq)
|
||||
[assembly: InternalsVisibleTo("Content.Benchmarks")]
|
||||
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
|
||||
[assembly: InternalsVisibleTo("Robust.Client.WebView")]
|
||||
[assembly: InternalsVisibleTo("Robust.Packaging")]
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
[module: SkipLocalsInit]
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Globalization;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
@@ -14,7 +14,8 @@ namespace Robust.Shared.Prototypes;
|
||||
internal sealed class YamlValidationContext :
|
||||
ISerializationContext,
|
||||
ITypeSerializer<EntityUid, ValueDataNode>,
|
||||
ITypeSerializer<NetEntity, ValueDataNode>
|
||||
ITypeSerializer<NetEntity, ValueDataNode>,
|
||||
ITypeSerializer<MapId, ValueDataNode>
|
||||
{
|
||||
public SerializationManager.SerializerProvider SerializerProvider { get; } = new();
|
||||
public bool WritingReadingPrototypes => true;
|
||||
@@ -93,4 +94,40 @@ internal sealed class YamlValidationContext :
|
||||
|
||||
return new ValueDataNode(value.Id.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
ValidationNode ITypeValidator<MapId, ValueDataNode>.Validate(
|
||||
ISerializationManager seri,
|
||||
ValueDataNode node,
|
||||
IDependencyCollection deps,
|
||||
ISerializationContext? context)
|
||||
{
|
||||
if (node.Value == "invalid")
|
||||
return new ValidatedValueNode(node);
|
||||
|
||||
return new ErrorNode(node, "Prototypes should not contain map ids", true);
|
||||
}
|
||||
|
||||
MapId ITypeReader<MapId, ValueDataNode>.Read(
|
||||
ISerializationManager seri,
|
||||
ValueDataNode node,
|
||||
IDependencyCollection deps,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? ctx,
|
||||
ISerializationManager.InstantiationDelegate<MapId>? instanceProvider)
|
||||
{
|
||||
return node.Value == "invalid" ? MapId.Nullspace : new MapId(int.Parse(node.Value));
|
||||
}
|
||||
|
||||
DataNode ITypeWriter<MapId>.Write(
|
||||
ISerializationManager seri,
|
||||
MapId value,
|
||||
IDependencyCollection deps,
|
||||
bool alwaysWrite,
|
||||
ISerializationContext? ctx)
|
||||
{
|
||||
if (value == MapId.Nullspace)
|
||||
return new ValueDataNode("invalid");
|
||||
|
||||
return new ValueDataNode(value.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
100
Robust.Shared/Serialization/NetBitArraySerializer.cs
Normal file
100
Robust.Shared/Serialization/NetBitArraySerializer.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
using NetSerializer;
|
||||
|
||||
namespace Robust.Shared.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Custom serializer implementation for <see cref="BitArray"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This type is necessary as, since .NET 10, the internal layout of <see cref="BitArray"/> was changed.
|
||||
/// The type now (internally) implements <see cref="ISerializable"/> for backwards compatibility with existing
|
||||
/// <c>BinaryFormatter</c> code, but NetSerializer does not support <see cref="ISerializable"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This code is designed to be backportable & network compatible with the previous behavior on .NET 9.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal sealed class NetBitArraySerializer : IDynamicTypeSerializer
|
||||
{
|
||||
// NOTE: MUST be a IDynamicTypeSerializer for compatibility!
|
||||
// Can be changed in the future.
|
||||
|
||||
// For reference, the layout of BitArray before .NET 10 was:
|
||||
// private int[] m_array;
|
||||
// private int m_length;
|
||||
// private int _version;
|
||||
// NetSerializer serialized these in the following order (sorted by name):
|
||||
// _version, m_array, m_length
|
||||
|
||||
public bool Handles(Type type)
|
||||
{
|
||||
return type == typeof(BitArray);
|
||||
}
|
||||
|
||||
public IEnumerable<Type> GetSubtypes(Type type)
|
||||
{
|
||||
return [typeof(int[]), typeof(int)];
|
||||
}
|
||||
|
||||
public void GenerateWriterMethod(Serializer serializer, Type type, ILGenerator il)
|
||||
{
|
||||
var method = typeof(NetBitArraySerializer).GetMethod("Write", BindingFlags.Static | BindingFlags.NonPublic)!;
|
||||
|
||||
// arg0: Serializer, arg1: Stream, arg2: value
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
|
||||
il.EmitCall(OpCodes.Call, method, null);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public void GenerateReaderMethod(Serializer serializer, Type type, ILGenerator il)
|
||||
{
|
||||
var method = typeof(NetBitArraySerializer).GetMethod("Read", BindingFlags.Static | BindingFlags.NonPublic)!;
|
||||
|
||||
// arg0: Serializer, arg1: stream, arg2: out value
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_2);
|
||||
|
||||
il.EmitCall(OpCodes.Call, method, null);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
private static void Write(Serializer serializer, Stream stream, BitArray value)
|
||||
{
|
||||
var intCount = (31 + value.Length) >> 5;
|
||||
var ints = new int[intCount];
|
||||
value.CopyTo(ints, 0);
|
||||
|
||||
serializer.SerializeDirect(stream, 0); // _version
|
||||
serializer.SerializeDirect(stream, ints); // m_array
|
||||
serializer.SerializeDirect(stream, value.Length); // m_length
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
private static void Read(Serializer serializer, Stream stream, out BitArray value)
|
||||
{
|
||||
serializer.DeserializeDirect<int>(stream, out _); // _version
|
||||
serializer.DeserializeDirect<int[]>(stream, out var array); // m_array
|
||||
serializer.DeserializeDirect<int>(stream, out var length); // m_length
|
||||
|
||||
value = new BitArray(array)
|
||||
{
|
||||
Length = length
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,8 @@ namespace Robust.Shared.Serialization
|
||||
CustomTypeSerializers = new[]
|
||||
{
|
||||
MappedStringSerializer.TypeSerializer,
|
||||
new NetMathSerializer()
|
||||
new NetMathSerializer(),
|
||||
new NetBitArraySerializer()
|
||||
}
|
||||
};
|
||||
_serializer = new Serializer(types, settings);
|
||||
|
||||
60
Robust.Shared/Toolshed/TypeParsers/ReflectionTypeParser.cs
Normal file
60
Robust.Shared/Toolshed/TypeParsers/ReflectionTypeParser.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Toolshed.Syntax;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Toolshed.TypeParsers;
|
||||
|
||||
/// <summary>
|
||||
/// This is custom type parser that uses reflection to search for constructible types that are the children of some base type.
|
||||
/// </summary>
|
||||
internal sealed class ReflectionTypeParser<TBase> : CustomTypeParser<Type> where TBase : class
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflection = default!;
|
||||
private Dictionary<string, Type>? _cache;
|
||||
private CompletionOption[]? _options;
|
||||
|
||||
[MemberNotNull(nameof(_cache))]
|
||||
[MemberNotNull(nameof(_options))]
|
||||
private void InitCache()
|
||||
{
|
||||
if (_cache != null && _options != null)
|
||||
return;
|
||||
|
||||
_cache = _reflection.GetAllChildren(typeof(TBase))
|
||||
.Where(x => x.HasParameterlessConstructor())
|
||||
.ToDictionary(x => x.Name, x => x);
|
||||
|
||||
_options = _cache.Keys.Select(x => new CompletionOption(x)).ToArray();
|
||||
}
|
||||
|
||||
public override bool TryParse(ParserContext ctx, [NotNullWhen(true)] out Type? result)
|
||||
{
|
||||
InitCache();
|
||||
var name = ctx.GetWord();
|
||||
if (name is null)
|
||||
{
|
||||
ctx.Error = new OutOfInputError();
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_cache.TryGetValue(name, out result))
|
||||
return true;
|
||||
|
||||
ctx.Error = new UnknownType(name);
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override CompletionResult? TryAutocomplete(ParserContext parserContext, CommandArgument? arg)
|
||||
{
|
||||
InitCache();
|
||||
return CompletionResult.FromHintOptions(_options, GetArgHint(arg));
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,13 @@ namespace Robust.UnitTesting.Client.Graphics
|
||||
new Angle(-Math.PI)
|
||||
})
|
||||
{
|
||||
|
||||
eyeManager.CurrentEye.Rotation = angle;
|
||||
|
||||
var worldAABB = eyeManager.GetWorldViewport();
|
||||
var worldPort = eyeManager.GetWorldViewbounds();
|
||||
|
||||
Assert.That(worldAABB.EqualsApprox(worldPort.CalcBoundingBox()), $"Invalid EyeRotation bounds found for {angle}: Expected {worldAABB} and received {worldPort.CalcBoundingBox()}");
|
||||
Assert.That(worldAABB.EqualsApprox(worldPort.CalcBoundingBox()),
|
||||
$"Invalid EyeRotation bounds found for {angle}: Expected {worldAABB} and received {worldPort.CalcBoundingBox()}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -196,10 +196,8 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
|
||||
|
||||
// Act
|
||||
sim.System<MoveEventTestSystem>().ResetCounters();
|
||||
sim.Transform(ent1).Anchored = true;
|
||||
xformSys.AnchorEntity(ent1);
|
||||
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner));
|
||||
sim.System<MoveEventTestSystem>().AssertMoved();
|
||||
traversal.Enabled = true;
|
||||
}
|
||||
|
||||
@@ -497,7 +495,7 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
// Act
|
||||
sim.System<MoveEventTestSystem>().FailOnMove = true;
|
||||
xformSys.Unanchor(ent1);
|
||||
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(mapSys.GetMap(coordinates.MapId)));
|
||||
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner));
|
||||
sim.System<MoveEventTestSystem>().FailOnMove = false;
|
||||
traversal.Enabled = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user