diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b7e3c040b7e..1adc0b803df 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -60,3 +60,6 @@ /Content.*/Atmos/ @Partmedia /Content.*/Botany/ @Partmedia +#Jezi +/Content.*/Medical @Jezithyr +/Content.*/Body @Jezithyr diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ae4c4e270f0..c8ce67f71dc 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,8 +5,8 @@ concurrency: on: workflow_dispatch: - schedule: - - cron: '0 10 * * *' +# schedule: +# - cron: '0 10 * * *' jobs: build: @@ -45,20 +45,22 @@ jobs: - name: Upload files to centcomm uses: appleboy/scp-action@master with: - host: centcomm.spacestation14.io - username: wizards-build-push - key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }} + host: ${{ secrets.PUBLISH_HOST }} + username: ${{ secrets.PUBLISH_USER }} + key: ${{ secrets.PUBLISH_KEY }} + port: ${{ secrets.PUBLISH_PORT }} source: "release/${{ github.sha }}" - target: "/home/wizards-build-push/builds_dir/builds/" + target: "/var/www/builds.delta-v.org/delta-v-rebase/builds/" strip_components: 1 - name: Update manifest JSON uses: appleboy/ssh-action@master with: - host: centcomm.spacestation14.io - username: wizards-build-push - key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }} - script: /home/wizards-build-push/push.ps1 ${{ github.sha }} + host: ${{ secrets.PUBLISH_HOST }} + username: ${{ secrets.PUBLISH_USER }} + key: ${{ secrets.PUBLISH_KEY }} + port: ${{ secrets.PUBLISH_PORT }} + script: /home/${{ secrets.PUBLISH_USER }}/publish/rebasepush.psi ${{ github.sha }} - name: Publish changelog (Discord) run: Tools/actions_changelogs_since_last_run.py diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml deleted file mode 100644 index 41f99f5794a..00000000000 --- a/.github/workflows/update-wiki.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Update Wiki - -on: - workflow_dispatch: - push: - branches: [ master, jsondump ] - paths: - - '.github/workflows/update-wiki.yml' - - 'Content.Shared/Chemistry/**.cs' - - 'Content.Server/Chemistry/**.cs' - - 'Content.Server/GuideGenerator/**.cs' - - 'Resources/Prototypes/Reagents/**.yml' - - 'Resources/Prototypes/Chemistry/**.yml' - - 'Resources/Prototypes/Recipes/Reactions/**.yml' - - 'RobustToolbox/' - -jobs: - update-wiki: - name: Build and Publish JSON blobs to wiki - runs-on: ubuntu-latest - - steps: - - name: Checkout Master - uses: actions/checkout@v3.6.0 - - - name: Setup Submodule - run: | - git submodule update --init --recursive - - - name: Pull Engine Updates - uses: space-wizards/submodule-dependency@v0.1.5 - - - name: Update Engine Submodules - run: | - cd RobustToolbox/ - git submodule update --init --recursive - - - name: Setup .NET Core - uses: actions/setup-dotnet@v3.2.0 - with: - dotnet-version: 7.0.x - - - name: Install Dependencies - run: dotnet restore - - - name: Build Project - run: dotnet build --configuration Release --no-restore /p:WarningsAsErrors=nullable /m - - - name: Generate JSON blobs for prototypes - run: dotnet ./bin/Content.Server/Content.Server.dll --cvar autogen.destination_file=prototypes.json - continue-on-error: true - - - name: Upload chem_prototypes.json to wiki - uses: jtmullen/mediawiki-edit-action@v0.1.1 - with: - wiki_text_file: ./bin/Content.Server/data/chem_prototypes.json - edit_summary: Update chem_prototypes.json via GitHub Actions - page_name: "${{ secrets.WIKI_PAGE_ROOT }}/chem_prototypes.json" - api_url: https://wiki.spacestation14.io/w/api.php - username: ${{ secrets.WIKI_BOT_USER }} - password: ${{ secrets.WIKI_BOT_PASS }} - - - name: Upload react_prototypes.json to wiki - uses: jtmullen/mediawiki-edit-action@v0.1.1 - with: - wiki_text_file: ./bin/Content.Server/data/react_prototypes.json - edit_summary: Update react_prototypes.json via GitHub Actions - page_name: "${{ secrets.WIKI_PAGE_ROOT }}/react_prototypes.json" - api_url: https://wiki.spacestation14.io/w/api.php - username: ${{ secrets.WIKI_BOT_USER }} - password: ${{ secrets.WIKI_BOT_PASS }} - diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs index 292759dc878..be45e57c8b1 100644 --- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs +++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs @@ -5,6 +5,7 @@ using Robust.Client.GameObjects; using Robust.Shared.Prototypes; using static Content.Shared.Access.Components.IdCardConsoleComponent; + namespace Content.Client.Access.UI { public sealed class IdCardConsoleBoundUserInterface : BoundUserInterface diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index 2d9b777abd7..41db0ad7ceb 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -4,7 +4,6 @@ using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.Player; -using Robust.Shared.Containers; using Robust.Shared.ContentPack; using Robust.Shared.GameStates; using Robust.Shared.Input.Binding; @@ -28,8 +27,8 @@ public sealed class ActionsSystem : SharedActionsSystem [Dependency] private readonly ISerializationManager _serialization = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; - public event Action? ActionAdded; - public event Action? ActionRemoved; + public event Action? OnActionAdded; + public event Action? OnActionRemoved; public event OnActionReplaced? ActionReplaced; public event Action? ActionsUpdated; public event Action? LinkActions; @@ -37,11 +36,8 @@ public sealed class ActionsSystem : SharedActionsSystem public event Action? ClearAssignments; public event Action>? AssignSlot; - /// - /// Queue of entities with that needs to be updated after - /// handling a state. - /// - private readonly Queue _actionHoldersQueue = new(); + private readonly List _removed = new(); + private readonly List<(EntityUid, BaseActionComponent?)> _added = new(); public override void Initialize() { @@ -49,87 +45,148 @@ public override void Initialize() SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); SubscribeLocalEvent(HandleComponentState); + + SubscribeLocalEvent(OnInstantHandleState); + SubscribeLocalEvent(OnEntityTargetHandleState); + SubscribeLocalEvent(OnWorldTargetHandleState); } - public override void Dirty(EntityUid? actionId) + private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args) { - var action = GetActionData(actionId); - if (_playerManager.LocalPlayer?.ControlledEntity != action?.AttachedEntity) + if (args.Current is not InstantActionComponentState state) return; - base.Dirty(actionId); - ActionsUpdated?.Invoke(); + BaseHandleState(uid, component, state); } - private void HandleComponentState(EntityUid uid, ActionsComponent component, ref ComponentHandleState args) + private void OnEntityTargetHandleState(EntityUid uid, EntityTargetActionComponent component, ref ComponentHandleState args) { - if (args.Current is not ActionsComponentState state) + if (args.Current is not EntityTargetActionComponentState state) return; - component.Actions.Clear(); - component.Actions.UnionWith(state.Actions); + component.Whitelist = state.Whitelist; + component.CanTargetSelf = state.CanTargetSelf; + BaseHandleState(uid, component, state); + } + + private void OnWorldTargetHandleState(EntityUid uid, WorldTargetActionComponent component, ref ComponentHandleState args) + { + if (args.Current is not WorldTargetActionComponentState state) + return; - _actionHoldersQueue.Enqueue(uid); + BaseHandleState(uid, component, state); } - protected override void AddActionInternal(EntityUid holderId, EntityUid actionId, IContainer container, ActionsComponent holder) + private void BaseHandleState(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent { - // Sometimes the client receives actions from the server, before predicting that newly added components will add - // their own shared actions. Just in case those systems ever decided to directly access action properties (e.g., - // action.Toggled), we will remove duplicates: - if (container.Contains(actionId)) - { - ActionReplaced?.Invoke(actionId); - } - else - { - base.AddActionInternal(holderId, actionId, container, holder); - } + component.Icon = state.Icon; + component.IconOn = state.IconOn; + component.IconColor = state.IconColor; + component.Keywords = new HashSet(state.Keywords); + component.Enabled = state.Enabled; + component.Toggled = state.Toggled; + component.Cooldown = state.Cooldown; + component.UseDelay = state.UseDelay; + component.Charges = state.Charges; + component.Container = EnsureEntity(state.Container, uid); + component.EntityIcon = EnsureEntity(state.EntityIcon, uid); + component.CheckCanInteract = state.CheckCanInteract; + component.ClientExclusive = state.ClientExclusive; + component.Priority = state.Priority; + component.AttachedEntity = EnsureEntity(state.AttachedEntity, uid); + component.AutoPopulate = state.AutoPopulate; + component.Temporary = state.Temporary; + component.ItemIconStyle = state.ItemIconStyle; + component.Sound = state.Sound; + + if (_playerManager.LocalPlayer?.ControlledEntity == component.AttachedEntity) + ActionsUpdated?.Invoke(); } - public override void AddAction(EntityUid holderId, EntityUid actionId, EntityUid? provider, ActionsComponent? holder = null, BaseActionComponent? action = null, bool dirty = true, IContainer? actionContainer = null) + protected override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null) { - if (!Resolve(holderId, ref holder, false)) + if (!ResolveActionData(actionId, ref action)) return; - action ??= GetActionData(actionId); - if (action == null) - { - Log.Warning($"No {nameof(BaseActionComponent)} found on entity {actionId}"); + base.UpdateAction(actionId, action); + if (_playerManager.LocalPlayer?.ControlledEntity != action.AttachedEntity) return; - } - dirty &= !action.ClientExclusive; - base.AddAction(holderId, actionId, provider, holder, action, dirty, actionContainer); - - if (holderId == _playerManager.LocalPlayer?.ControlledEntity) - ActionAdded?.Invoke(actionId); + ActionsUpdated?.Invoke(); } - public override void RemoveAction(EntityUid holderId, EntityUid? actionId, ActionsComponent? comp = null, BaseActionComponent? action = null, bool dirty = true) + private void HandleComponentState(EntityUid uid, ActionsComponent component, ref ComponentHandleState args) { - if (GameTiming.ApplyingState) + if (args.Current is not ActionsComponentState state) return; - if (!Resolve(holderId, ref comp, false)) - return; + _added.Clear(); + _removed.Clear(); + var stateEnts = EnsureEntitySet(state.Actions, uid); + foreach (var act in component.Actions) + { + if (!stateEnts.Contains(act) && !IsClientSide(act)) + _removed.Add(act); + } + component.Actions.ExceptWith(_removed); + + foreach (var actionId in stateEnts) + { + if (!actionId.IsValid()) + continue; + + if (!component.Actions.Add(actionId)) + continue; - if (actionId == null) + TryGetActionData(actionId, out var action); + _added.Add((actionId, action)); + } + + if (_playerManager.LocalPlayer?.ControlledEntity != uid) return; - action ??= GetActionData(actionId); + foreach (var action in _removed) + { + OnActionRemoved?.Invoke(action); + } + + _added.Sort(ActionComparer); + + foreach (var action in _added) + { + OnActionAdded?.Invoke(action.Item1); + } + + ActionsUpdated?.Invoke(); + } + + public static int ActionComparer((EntityUid, BaseActionComponent?) a, (EntityUid, BaseActionComponent?) b) + { + var priorityA = a.Item2?.Priority ?? 0; + var priorityB = b.Item2?.Priority ?? 0; + if (priorityA != priorityB) + return priorityA - priorityB; + + priorityA = a.Item2?.Container?.Id ?? 0; + priorityB = b.Item2?.Container?.Id ?? 0; + return priorityA - priorityB; + } - if (action is { ClientExclusive: false }) + protected override void ActionAdded(EntityUid performer, EntityUid actionId, ActionsComponent comp, + BaseActionComponent action) + { + if (_playerManager.LocalPlayer?.ControlledEntity != performer) return; - dirty &= !action?.ClientExclusive ?? true; - base.RemoveAction(holderId, actionId, comp, action, dirty); + OnActionAdded?.Invoke(actionId); + } - if (_playerManager.LocalPlayer?.ControlledEntity != holderId) + protected override void ActionRemoved(EntityUid performer, EntityUid actionId, ActionsComponent comp, BaseActionComponent action) + { + if (_playerManager.LocalPlayer?.ControlledEntity != performer) return; - if (action == null || action.AutoRemove) - ActionRemoved?.Invoke(actionId.Value); + OnActionRemoved?.Invoke(actionId); } public IEnumerable<(EntityUid Id, BaseActionComponent Comp)> GetClientActions() @@ -180,9 +237,6 @@ public void TriggerAction(EntityUid actionId, BaseActionComponent action) return; } - if (action.Provider != null && Deleted(action.Provider)) - return; - if (action is not InstantActionComponent instantAction) return; @@ -195,33 +249,11 @@ public void TriggerAction(EntityUid actionId, BaseActionComponent action) } else { - var request = new RequestPerformActionEvent(actionId); + var request = new RequestPerformActionEvent(GetNetEntity(actionId)); EntityManager.RaisePredictiveEvent(request); } } - /*public void SaveActionAssignments(string path) - { - - // Currently only tested with temporary innate actions (i.e., mapping actions). No guarantee it works with - // other actions. If its meant to be used for full game state saving/loading, the entity that provides - // actions needs to keep the same uid. - - var sequence = new SequenceDataNode(); - - foreach (var (action, assigns) in Assignments.Assignments) - { - var slot = new MappingDataNode(); - slot.Add("action", _serializationManager.WriteValue(action)); - slot.Add("assignments", _serializationManager.WriteValue(assigns)); - sequence.Add(slot); - } - - using var writer = _resourceManager.UserData.OpenWriteText(new ResourcePath(path).ToRootedPath()); - var stream = new YamlStream { new(sequence.ToSequenceNode()) }; - stream.Save(new YamlMappingFix(new Emitter(writer)), false); - }*/ - /// /// Load actions and their toolbar assignments from a file. /// @@ -255,8 +287,8 @@ public void LoadActionAssignments(string path, bool userData) var action = _serialization.Read(actionNode, notNullableOverride: true); var actionId = Spawn(null); - AddComp(actionId, action); - AddAction(user, actionId, null); + AddComp(actionId, action); + AddActionDirect(user, actionId); if (map.TryGet("name", out var nameNode)) _metaData.SetEntityName(actionId, nameNode.Value); @@ -276,95 +308,6 @@ public void LoadActionAssignments(string path, bool userData) AssignSlot?.Invoke(assignments); } - public override void Update(float frameTime) - { - base.Update(frameTime); - - if (_actionHoldersQueue.Count == 0) - return; - - var removed = new List(); - var added = new List<(EntityUid Id, BaseActionComponent Comp)>(); - var query = GetEntityQuery(); - var queue = new Queue(_actionHoldersQueue); - _actionHoldersQueue.Clear(); - - while (queue.TryDequeue(out var holderId)) - { - if (!TryGetContainer(holderId, out var container) || container.ExpectedEntities.Count > 0) - { - _actionHoldersQueue.Enqueue(holderId); - continue; - } - - if (!query.TryGetComponent(holderId, out var holder)) - continue; - - removed.Clear(); - added.Clear(); - - foreach (var (act, data) in holder.OldClientActions.ToList()) - { - if (data.ClientExclusive) - continue; - - if (!holder.Actions.Contains(act)) - { - holder.OldClientActions.Remove(act); - if (data.AutoRemove) - removed.Add(act); - } - } - - // Anything that remains is a new action - foreach (var newAct in holder.Actions) - { - if (!TryGetActionData(newAct, out var serverData)) - continue; - - if (!holder.OldClientActions.ContainsKey(newAct)) - added.Add((newAct, serverData)); - - holder.OldClientActions[newAct] = new ActionMetaData(serverData.ClientExclusive, serverData.AutoRemove); - } - - if (_playerManager.LocalPlayer?.ControlledEntity != holderId) - return; - - foreach (var action in removed) - { - ActionRemoved?.Invoke(action); - } - - added.Sort(static (a, b) => - { - if (a.Comp.Priority != b.Comp.Priority) - return a.Comp.Priority - b.Comp.Priority; - - if (a.Comp.Provider != b.Comp.Provider) - { - if (a.Comp.Provider == null) - return -1; - - if (b.Comp.Provider == null) - return 1; - - // uid to int casting... it says "Do NOT use this in content". You can't tell me what to do. - return (int) a.Comp.Provider - (int) b.Comp.Provider; - } - - return 0; - }); - - foreach (var action in added) - { - ActionAdded?.Invoke(action.Item1); - } - - ActionsUpdated?.Invoke(); - } - } - public record struct SlotAssignment(byte Hotbar, byte Slot, EntityUid ActionId); } } diff --git a/Content.Client/Administration/AdminNameOverlay.cs b/Content.Client/Administration/AdminNameOverlay.cs index 8afee7f3669..c21ba2e32ca 100644 --- a/Content.Client/Administration/AdminNameOverlay.cs +++ b/Content.Client/Administration/AdminNameOverlay.cs @@ -35,20 +35,21 @@ protected override void Draw(in OverlayDrawArgs args) foreach (var playerInfo in _system.PlayerList) { + var entity = _entityManager.GetEntity(playerInfo.NetEntity); + // Otherwise the entity can not exist yet - if (!_entityManager.EntityExists(playerInfo.EntityUid)) + if (entity == null || !_entityManager.EntityExists(entity)) { continue; } - var entity = playerInfo.EntityUid.Value; // if not on the same map, continue - if (_entityManager.GetComponent(entity).MapID != _eyeManager.CurrentMap) + if (_entityManager.GetComponent(entity.Value).MapID != _eyeManager.CurrentMap) { continue; } - var aabb = _entityLookup.GetWorldAABB(entity); + var aabb = _entityLookup.GetWorldAABB(entity.Value); // if not on screen, continue if (!aabb.Intersects(in viewport)) diff --git a/Content.Client/Administration/Systems/AdminVerbSystem.cs b/Content.Client/Administration/Systems/AdminVerbSystem.cs index e37b51af526..d08ebc0fcef 100644 --- a/Content.Client/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Client/Administration/Systems/AdminVerbSystem.cs @@ -24,12 +24,14 @@ private void AddAdminVerbs(GetVerbsEvent args) // View variables verbs if (_clientConGroupController.CanViewVar()) { - Verb verb = new(); - verb.Category = VerbCategory.Debug; - verb.Text = "View Variables"; - verb.Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/vv.svg.192dpi.png")); - verb.Act = () => _clientConsoleHost.ExecuteCommand($"vv {args.Target}"); - verb.ClientExclusive = true; // opening VV window is client-side. Don't ask server to run this verb. + Verb verb = new() + { + Category = VerbCategory.Debug, + Text = "View Variables", + Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/vv.svg.192dpi.png")), + Act = () => _clientConsoleHost.ExecuteCommand($"vv {GetNetEntity(args.Target)}"), + ClientExclusive = true // opening VV window is client-side. Don't ask server to run this verb. + }; args.Verbs.Add(verb); } } diff --git a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml index eac7e37a86d..311829e8b2b 100644 --- a/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml +++ b/Content.Client/Administration/UI/AdminRemarks/AdminMessagePopupWindow.xaml @@ -1,13 +1,9 @@ - - - - +