SmartlyDressedGames / Unturned-3.x-Community

Community portion of the Unturned-3.x repo. If you have access to the source code you can find it here:
https://github.com/SmartlyDressedGames/Unturned-3.x/
88 stars 18 forks source link

Feature Requests 2 #1002

Closed erik7302 closed 1 year ago

erik7302 commented 5 years ago

1 sendUIEffectAppendText | Instead of replacing the text, append text to the text object. 2 sendUIEffectAttach | Send Effects that follow the position of the player. i would to attach things like Trails, Particles, Billboard GUIs, etc to the player. 3 Events for sliders, checkboxs/toggle and dropdown. 4 Add GUID Object to Barricades/Vehicles/Item Classes. also save it, Maybe: 5 sendTransparency | sends transparency in a range of 0 and 1 where 0 is totally invisible, 1 is fully visible and as 0.5 is semi visible, that affects the player and all that he have equipped.

QnAisbest commented 5 years ago

Stupid requests

Pigeon-cmd commented 5 years ago

I might sound crazy, but for 1, you could store what the text was per player in a Dictionary of CSteamId's and Strings, and then just pull from that dictionary and append to it

2 would be interesting (might already be possible?)

3 isn't too necessary

4 is more of a plugin thing in my opinion, If you're using AviRocket, he attaches a GUID as a component to every Vehicle, Structure and Barricade and you could theoretically just use his, or make your own way

erik7302 commented 5 years ago

I might sound crazy, but for 1, you could store what the text was per player in a Dictionary of CSteamId's and Strings, and then just pull from that dictionary and append to it

2 would be interesting (might already be possible?)

3 isn't too necessary

4 is more of a plugin thing in my opinion, If you're using AviRocket, he attaches a GUID as a component to every Vehicle, Structure and Barricade and you could theoretically just use his, or make your own way

1 the thing is that you cant send more than 255(i think was the limit) string length with sendUIEffectText to a ui 4 i want to avoid patching unturned loading and saving, Also only with modules

SDGNelson commented 5 years ago

Future updates after some netcode improvements (TBD) will give more control over UI effects, but in the meantime I've adjusted how strings are replicated. Now the high bit indicates whether to use one or two bytes, and the limit has been raised to 2048 UTF-8 bytes, essentially 2048 English characters or ~1024 non-English characters.

erik7302 commented 5 years ago

Future updates after some netcode improvements (TBD) will give more control over UI effects, but in the meantime I've adjusted how strings are replicated. Now the high bit indicates whether to use one or two bytes, and the limit has been raised to 2048 UTF-8 bytes, essentially 2048 English characters or ~1024 non-English characters.

What is TBD and an example and when will it be added(aprox)? if i propose code like methods,calls,events for unturned can be implemented?

SDGNelson commented 5 years ago

By TBD (to be determined) I meant that the details and time they will be implemented are unknown at the moment. I'm open to suggestions, and there are a few open issues here with proposals I intend to implement for EffectUIs.

erik7302 commented 5 years ago

By TBD (to be determined) I meant that the details and time they will be implemented are unknown at the moment. I'm open to suggestions, and there are a few open issues here with proposals I intend to implement for EffectUIs.

I propose this:

namespace SDG.Unturned { public class EffectManager : SteamCaller { private static List sliderComponents = new List(); private static List dropdownComponents = new List(); private static List toggleComponents = new List();

    public delegate void EffectSliderChangedHandler(Player player, string sliderName, float value);
    public static EffectManager.EffectSliderChangedHandler onEffectSliderChanged;
    public delegate void EffectDropdownChangedHandler(Player player, string dropdownName, int value);
    public static EffectManager.EffectDropdownChangedHandler onEffectDropdownChanged;
    public delegate void EffectToggleChangedHandler(Player player, string toggleName, bool value);
    public static EffectManager.EffectToggleChangedHandler onEffectToggleChanged;

    public static Transform createUIEffect(ushort id, short key)
    {
        EffectAsset effectAsset = (EffectAsset)Assets.find(EAssetType.EFFECT, id);
        if (effectAsset == null)
        {
            return null;
        }
        Transform transform = UnityEngine.Object.Instantiate<GameObject>(effectAsset.effect).transform;
        transform.name = id.ToString();
        transform.SetParent(Level.effects);
        if (key == -1)
        {
            if (effectAsset.lifetime > 1.401298E-45f)
            {
                EffectManager.Destroy(transform.gameObject, effectAsset.lifetime + UnityEngine.Random.Range(-effectAsset.lifetimeSpread, effectAsset.lifetimeSpread));
            }
        }
        else
        {
            GameObject element;
            if (EffectManager.indexedUIEffects.TryGetValue(key, out element))
            {
                EffectManager.Destroy(element);
                EffectManager.indexedUIEffects.Remove(key);
            }
            EffectManager.indexedUIEffects.Add(key, transform.gameObject);
        }
        EffectManager.hookButtonsInUIEffect(transform);
        EffectManager.hookInputFieldsInUIEffect(transform);
        EffectManager.hookSlidersInUIEffect(transform);
        EffectManager.hookDropdownsInUIEffect(transform);
        EffectManager.hookTogglesInUIEffect(transform);
        EffectManager.gatherFormattingForUIEffect(transform);
        EffectManager.formatPluginHotkeysIntoUIEffect(transform);
        return transform;
    }

    public static void hookSlidersInUIEffect(Transform effect)
    {
        EffectManager.sliderComponents.Clear();
        effect.GetComponentsInChildren<Slider>(EffectManager.sliderComponents);
        foreach (Slider slider in EffectManager.sliderComponents)
        {
            PluginSliderListener pluginButtonListener = slider.gameObject.AddComponent<PluginSliderListener>();
            pluginButtonListener.targetSlider = slider;
        }
    }

    public static void hookDropdownsInUIEffect(Transform effect)
    {
        EffectManager.dropdownComponents.Clear();
        effect.GetComponentsInChildren<Dropdown>(EffectManager.dropdownComponents);
        foreach (Dropdown dropdown in EffectManager.dropdownComponents)
        {
            PluginDropdownListener pluginButtonListener = dropdown.gameObject.AddComponent<PluginDropdownListener>();
            pluginButtonListener.targetDropdown = dropdown;
        }
    }

    public static void hookTogglesInUIEffect(Transform effect)
    {
        EffectManager.dropdownComponents.Clear();
        effect.GetComponentsInChildren<Toggle>(EffectManager.toggleComponents);
        foreach (Toggle toggle in EffectManager.toggleComponents)
        {
            PluginToggleListener pluginButtonListener = toggle.gameObject.AddComponent<PluginToggleListener>();
            pluginButtonListener.targetToggle = toggle;
        }
    }

    public static void sendEffectSliderChanged(string sliderName, float value)
    {
        EffectManager.manager.channel.send("tellEffectSliderChanged", ESteamCall.SERVER, ESteamPacket.UPDATE_RELIABLE_BUFFER, new object[]
        {
            sliderName, value
        });
    }

    [SteamCall(ESteamCallValidation.SERVERSIDE)]
    public void tellEffectSliderChanged(CSteamID steamID, string sliderName, float value)
    {
        Player player = PlayerTool.getPlayer(steamID);
        if (player == null)
        {
            return;
        }
        if (!player.tryToPerformRateLimitedAction())
        {
            return;
        }
        if (EffectManager.onEffectSliderChanged != null)
        {
            EffectManager.onEffectSliderChanged(player, sliderName, value);
        }
    }

    public static void sendEffectDropdownChanged(string dropdownName, int value)
    {
        EffectManager.manager.channel.send("tellEffectDropdownChanged", ESteamCall.SERVER, ESteamPacket.UPDATE_RELIABLE_BUFFER, new object[]
        {
            dropdownName, value
        });
    }

    [SteamCall(ESteamCallValidation.SERVERSIDE)]
    public void tellEffectDropdownChanged(CSteamID steamID, string dropdownName, int value)
    {
        Player player = PlayerTool.getPlayer(steamID);
        if (player == null)
        {
            return;
        }
        if (!player.tryToPerformRateLimitedAction())
        {
            return;
        }
        if (EffectManager.onEffectDropdownChanged != null)
        {
            EffectManager.onEffectDropdownChanged(player, dropdownName, value);
        }
    }

    public static void sendEffectToggleChanged(string toggleName, bool value)
    {
        EffectManager.manager.channel.send("tellEffectToggleChanged", ESteamCall.SERVER, ESteamPacket.UPDATE_RELIABLE_BUFFER, new object[]
        {
            toggleName, value
        });
    }

    [SteamCall(ESteamCallValidation.SERVERSIDE)]
    public void tellEffectToggleChanged(CSteamID steamID, string toggleName, bool value)
    {
        Player player = PlayerTool.getPlayer(steamID);
        if (player == null)
        {
            return;
        }
        if (!player.tryToPerformRateLimitedAction())
        {
            return;
        }
        if (EffectManager.onEffectToggleChanged != null)
        {
            EffectManager.onEffectToggleChanged(player, toggleName, value);
        }
    }

    public static void sendUIEffectText(short key, CSteamID steamID, bool reliable, string childName, string text, bool append = false)
    {
        EffectManager.manager.channel.send("tellUIEffectText", steamID, (!reliable) ? ESteamPacket.UPDATE_UNRELIABLE_BUFFER : ESteamPacket.UPDATE_RELIABLE_BUFFER, new object[]
        {key,childName,text, append});
    }

    [SteamCall(ESteamCallValidation.ONLY_FROM_SERVER)]
    public void tellUIEffectText(CSteamID steamID, short key, string childName, string text, bool append)
    {
        GameObject gameObject;
        if (!EffectManager.indexedUIEffects.TryGetValue(key, out gameObject))
        {
            Debug.LogFormat("tellUIEffectText: key {0} not found (childName {1} text {2})", new object[]
            {
                key,
                childName,
                text
            });
            return;
        }
        if (gameObject == null)
        {
            Debug.LogFormat("tellUIEffectText: key {0} was destroyed (childName {1} text {2})", new object[]
            {
                key,
                childName,
                text
            });
            return;
        }
        Transform transform = gameObject.transform.FindChildRecursive(childName);
        if (transform == null)
        {
            Debug.LogFormat("tellUIEffectText: childName '{0}' not found (key {1} text {2})", new object[]
            {
                childName,
                key,
                text
            });
            return;
        }
        Text component = transform.GetComponent<Text>();
        if (component == null)
        {
            Debug.LogFormat("tellUIEffectText: '{0}' does not have a text component (key {1} text {2})", new object[]
            {
                childName,
                key,
                text
            });
            return;
        }
        if (append)
        {
            component.text += text;
        }
        else
        {
            component.text = text;
        }
    }

    public static void sendUIEffectFillImage(short key, CSteamID steamID, bool reliable, string childName, float amount)
    {
        EffectManager.manager.channel.send("tellUIEffectFillImage", steamID, (!reliable) ? ESteamPacket.UPDATE_UNRELIABLE_BUFFER : ESteamPacket.UPDATE_RELIABLE_BUFFER, new object[]
        {key,childName,amount,});
    }

    [SteamCall(ESteamCallValidation.ONLY_FROM_SERVER)]
    public void tellUIEffectFillImage(CSteamID steamID, short key, string childName, float value)
    {
        GameObject gameObject;
        if (!EffectManager.indexedUIEffects.TryGetValue(key, out gameObject))
        {
            Debug.LogFormat("tellUIEffectFillImage: key {0} not found (childName {1} value {2})", new object[]
            {
                key,
                childName,
                value
            });
            return;
        }
        if (gameObject == null)
        {
            Debug.LogFormat("tellUIEffectFillImage: key {0} was destroyed (childName {1} value {2})", new object[]
            {
                key,
                childName,
                value
            });
            return;
        }
        Transform transform = gameObject.transform.FindChildRecursive(childName);
        if (transform == null)
        {
            Debug.LogFormat("tellUIEffectFillImage: childName '{0}' not found (key {1} value {2})", new object[]
            {
                childName,
                key,
                value
            });
            return;
        }
        Image component = transform.GetComponent<Image>();
        if (component == null)
        {
            Debug.LogFormat("tellUIEffectFillImage: '{0}' does not have a image component (key {1} value {2})", new object[]
            {
                childName,
                key,
                value
            });
            return;
        }
        if(component.type != Image.Type.Filled)
        {
            Debug.LogFormat("tellUIEffectFillImage: '{0}' is not a filled image type (key {1} value {2})", new object[]
            {
                childName,
                key,
                value
            });
            return;
        }
        component.fillAmount = value;
    }

    public static void sendUIEffectPlayAnimation(short key, CSteamID steamID, bool reliable, string childName, string animationName, string animationLayerName = "Base Layer")
    {
        EffectManager.manager.channel.send("tellUIEffectPlayAnimation", steamID, (!reliable) ? ESteamPacket.UPDATE_UNRELIABLE_BUFFER : ESteamPacket.UPDATE_RELIABLE_BUFFER, new object[]
        {key,childName,animationName,animationLayerName});
    }

    [SteamCall(ESteamCallValidation.ONLY_FROM_SERVER)]
    public void tellUIEffectPlayAnimation(CSteamID steamID, short key, string childName, string animationName, string animationLayerName)
    {
        GameObject gameObject;
        if (!EffectManager.indexedUIEffects.TryGetValue(key, out gameObject))
        {
            Debug.LogFormat("tellUIEffectPlayAnimation: key {0} not found (childName {1} name {2})", new object[]
            {
                key,
                childName,
                animationName
            });
            return;
        }
        if (gameObject == null)
        {
            Debug.LogFormat("tellUIEffectPlayAnimation: key {0} was destroyed (childName {1} name {2})", new object[]
            {
                key,
                childName,
                animationName
            });
            return;
        }
        Transform transform = gameObject.transform.FindChildRecursive(childName);
        if (transform == null)
        {
            Debug.LogFormat("tellUIEffectPlayAnimation: childName '{0}' not found (key {1} name {2})", new object[]
            {
                childName,
                key,
                animationName
            });
            return;
        }
        Animator component = transform.GetComponent<Animator>();
        if (component == null)
        {
            Debug.LogFormat("tellUIEffectPlayAnimation: '{0}' does not have a animator component (key {1} name {2})", new object[]
            {
                childName,
                key,
                animationName
            });
            return;
        }
        if (!component.HasState(component.GetLayerIndex(animationLayerName), Animator.StringToHash(animationName)))
        {
            Debug.LogFormat("tellUIEffectPlayAnimation: The animation state '{0}' does not exits in layer {1} (key {2})", new object[]
            {
                animationName,
                animationLayerName,
                key
            });
            return;
        }
        component.Play(animationName, component.GetLayerIndex(animationLayerName));
    }

    public static void sendUIEffectDropdownOptions(short key, CSteamID steamID, bool reliable, string childName, string[] options)
    {
        EffectManager.manager.channel.send("tellUIEffectDropdownOptions", steamID, (!reliable) ? ESteamPacket.UPDATE_UNRELIABLE_BUFFER : ESteamPacket.UPDATE_RELIABLE_BUFFER, new object[]
        {key,childName,options});
    }

    [SteamCall(ESteamCallValidation.ONLY_FROM_SERVER)]
    public void tellUIEffectDropdownOptions(CSteamID steamID, short key, string childName, string[] options)
    {
        GameObject gameObject;
        if (!EffectManager.indexedUIEffects.TryGetValue(key, out gameObject))
        {
            Debug.LogFormat("tellUIEffectDropdownOptions: key {0} not found (childName {1} value {2})", new object[]
            {
                key,
                childName,
                options
            });
            return;
        }
        if (gameObject == null)
        {
            Debug.LogFormat("tellUIEffectDropdownOptions: key {0} was destroyed (childName {1} value {2})", new object[]
            {
                key,
                childName,
                options
            });
            return;
        }
        Transform transform = gameObject.transform.FindChildRecursive(childName);
        if (transform == null)
        {
            Debug.LogFormat("tellUIEffectDropdownOptions: childName '{0}' not found (key {1} value {2})", new object[]
            {
                childName,
                key,
                options
            });
            return;
        }
        Dropdown component = transform.GetComponent<Dropdown>();
        if (component == null)
        {
            Debug.LogFormat("tellUIEffectDropdownOptions: '{0}' does not have a dropdown component (key {1} value {2})", new object[]
            {
                childName,
                key,
                options
            });
            return;
        }
        component.options.Clear();
        foreach(string s in options)
        {
            component.options.Add(new Dropdown.OptionData(s));
        }
    }
}

} namespace SDG.Unturned { public class PluginToggleListener : MonoBehaviour { public PluginToggleListener() {

    }

    protected void Start()
    {
        if (this.targetToggle != null)
        {
            this.targetToggle.onValueChanged.AddListener(new UnityAction<bool>(this.onTargetToggleChanged));
        }
    }

    private void onTargetToggleChanged(bool value)
    {
        if (this.targetToggle != null)
        {
            EffectManager.sendEffectToggleChanged(this.targetToggle.name, value);
        }
    }

    public Toggle targetToggle;

}

}namespace SDG.Unturned { public class PluginDropdownListener : MonoBehaviour { public PluginDropdownListener() {

    }

    protected void Start()
    {
        if (this.targetDropdown != null)
        {
            this.targetDropdown.onValueChanged.AddListener(new UnityAction<int>(this.onTargetDropdownChanged));
        }
    }

    private void onTargetDropdownChanged(int value)
    {
        if (this.targetDropdown != null)
        {
            EffectManager.sendEffectDropdownChanged(this.targetDropdown.name, value);
        }
    }

    public Dropdown targetDropdown;
}

}namespace SDG.Unturned { public class PluginSliderListener : MonoBehaviour { public PluginSliderListener() {

    }

    protected void Start()
    {
        if (this.targetSlider != null)
        {
            this.targetSlider.onValueChanged.AddListener(new UnityAction<float>(this.onTargetSliderChanged));
        }
    }

    private void onTargetSliderChanged(float value)
    {
        if (this.targetSlider != null)
        {
            EffectManager.sendEffectSliderChanged(this.targetSlider.name, value);
        }
    }

    public Slider targetSlider;
}

}

erik7302 commented 5 years ago

any updates on this?

SDGNelson commented 5 years ago

1/2/3/5 depend on the future netcode improvements, but might happen for barricades and structures. Vehicles should already have an instance ID saved I believe.

zientix commented 3 years ago

Any updates on this?

DiFFoZ commented 3 years ago

Any updates on this?

https://trello.com/c/crkUBc1q/9-custom-ui - possible will be implemented on this year

zientix commented 3 years ago

I hope so, thanks for that

SDGNelson commented 1 year ago

Closing because the issues list was not really intended for feature requests. Sorry to close this discussion - I do WANT to fulfill all (most?) requests, but it is a matter of prioritization. Constantly being reminded of all the pending requests when checking issues is stressful. I will keep the request in mind. On GitHub maybe the Ideas section of Discussions would be the best place.