in-game radii view for triggers

Discussions about Coding and Scripting
OwYeaW
Experienced
Posts: 81
Joined: Fri Jan 09, 2015 4:24 pm

Re: in-game radii view for triggers

Post by OwYeaW »

just tested the difference with STY_Normal and STY_Translucent; no difference in fps at all

anyways, i managed to get something "playable" working now
basically i made 4 meshes, each with different Z
so depending on the height/width ratio of the trigger's radii they are getting the appropriate mesh

i might make more than 4 meshes to distribute over the range of different ratio's to further reduce fps impact
here is a video of the current status: https://streamable.com/7rsnd
these are TriggeredDeaths, i didnt really find a good texture yet, maybe you know some fitting textures? (one for Triggers, Kickers and TriggeredDeaths)
they dont have to be animated per se, just gotta fit


EDIT:

now probably for my final question :):

currently the only way i was able to spawn the cylinder actor (client sided ofc) was by modifying the Triggers via the main mutator on the server
done 2 ways:

Code: Select all

Foreach AllActors(class'Triggers', TRIG)
	TRIG.bHidden = false;
the downside on this is all the trigger's sprites will be shown to the players and im guessing theres some limit at how many actors can be shown on a client, because other actors seemed to be disappearing on my client
(+ im guessing it gives the server some extra load to work with too, right?)

Code: Select all

Foreach AllActors(class'Triggers', TRIG)
	TRIG.bAlwaysRelevant = true;
this one really gives the server a hard time on maps with many Triggers obviously, so this aint really an option too

the reason why im trying these methods is because for some reason the client cant find the Triggers if they have bHidden=true
and also; if the Triggers would have bHidden=false, the client still cant see those Triggers that have its center located in the void

so the question is; is there a way for a client to be able to see Actors with bHidden=true and even if they are located in the void?
(a quick idea i got is maybe let the server create some dynamic array struct with all the locations and radii loaded at the start of the game and stored for when players activate this "Show Trigger Collisions" feature? lol i feel like there should be an easier way for all this)
User avatar
Feralidragon
Godlike
Posts: 5489
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: in-game radii view for triggers

Post by Feralidragon »

For the texture, I don't have any good ideas either, it's one of those things you have to try out some, or create one of your own, so you end up with one that you like.
Since it's client side, you could even offer the clients to choose the texture by themselves, among a specific set of them, or not, up to you.

As for triggers visibility, that has to do with replication rules, on whether or not an actor is "relevant" enough to be sent to the clients.
There is a specific set of rules or steps for the engine to decide to whether or not send the info about a specific actor spawned server-side to the client, and to put it simply: generally, if you cannot see or hear the actor, and you don't "own" it (like a weapon), and the actor is not static (like Keypoints) and is not set to be always relevant bAlwaysRelevant=False, it's not relevant to you.

By default, a Trigger cannot be seen (bHidden=True), cannot be heard (has no sounds playing), it's not owned by any player, it's not static and has bAlwaysRelevant=False, so the clients are not even aware that triggers even exist on their end, since they are not replicated from the server.

This means that when you touch a trigger, your client-self is not even aware that it touched anything at all, whereas your player copy in the server, does touch something, which triggers a behavior elsewhere, and is the result of that behavior that ends up being replicated back to you (the client), such as getting killed, or hearing a sound, or opening a door, or any other.

As such, what you have to do is for the client to be aware of them in some way.
What you proposed could be a way, and in terms of used bandwidth, as well communication channels with the server, it would be very efficient, since replicating a structure from a single actor is, by far, more efficient than replicating multiple actors, but it's also more difficult to implement and maintain over time, and there may be problems you will need to sort out later on.

The simplest way, but also the least efficient network-wise, would be to make them all relevant like you did, but you can lessen the impact by setting the triggers to only be relevant when they are in view for instance, rather than being relevant all the time.

You already achieved this by setting them as bHidden=False, with the side effect of having the actor texture visible, which in that case you can workaround by doing 1 of 2 things:
- setting the DrawScale to zero or near zero (for relevancy checks the size of a texture or mesh does not matter, it could as well be sub-atomic in size);
- setting a texture which when rendered it becomes invisible: a fully masked texture, or a fully black texture but rendered as translucent.

That way, visually you won't actually see the triggers as far as the human eye goes, but for the engine it's as visible to you as before, so it remains relevant to the client.
OwYeaW
Experienced
Posts: 81
Joined: Fri Jan 09, 2015 4:24 pm

Re: in-game radii view for triggers

Post by OwYeaW »

i just started doing the (non dynamic) array struct method and of course ran into some mysterious issue

basically im letting the server spawn an actor that creates this struct:

Code: Select all

struct TriggerStruct
{
	var	Triggers Trigger;
	var	Texture Texture;
	var vector Location;
};
var TriggerStruct TS[10000];
then, when spawned; it fills the array (im showing a simplified version for example purpose)

Code: Select all

event Spawned()
{
	local int i;
	local Triggers TRIG;

	Foreach AllActors(class'Triggers', TRIG)
	{
		TRIG.bAlwaysRelevant = true;
		TRIG.bGameRelevant = true;
		TRIG.bHidden = false;

		TS[i].Trigger = TRIG;
		TS[i].Location = TRIG.Location;
		i++;
	}
}
now, i made a client sided function:

Code: Select all

simulated function DrawTriggers(PlayerPawn PP)
{
	local int i;

	for(i = 0; i < 10000; i++)
	{
		PP.ClientMessage("TS["$i$"].Trigger: " $ TS[i].Trigger);
		PP.ClientMessage("TS["$i$"].Location: " $ TS[i].Location);
		PP.ClientMessage("TS["$i$"].Texture: " $ TS[i].Texture);
	}
}
i can see the Texture, Location but not the Trigger itself....
even when doing bHidden=false, bAlwaysRelevant=true, or even when placing actors with these values in the map
its value is always None, but why?

i made sure the whole struct is replicated, and the replication obviously works since i can see the Location and Texture

Code: Select all

replication
{
	reliable if(Role == ROLE_Authority)
		TS;
}
and btw, the server can see the value of TS.Trigger, so it surely aint empty


EDIT:
i want to mention that the bHidden=false method will make the server unplayable in maps with many triggers
also via this method the client still cant see the triggers when its center is in BSP/void

so im hoping this array stuff will work, if it can actually replicate the triggers
User avatar
sektor2111
Godlike
Posts: 6403
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: in-game radii view for triggers

Post by sektor2111 »

Note: In a MH game session I did an explanation to a player. Using a few console commands I made triggers visible in original using their default sprites. Actually Player "Know" about a trigger because it's an actor Natively replicated and
Trigger_Net.PNG
Trigger_Net.PNG (8.12 KiB) Viewed 782 times
If it needs prioritized it's a matter of what DrawType has trigger because Actor's replication works depending on what sort of Actor is the one in cause.
ELSE... I spawn some "decorations" for Hunters in custom DM games pointing routecache[x] points, I have to admit that replication is poor at a moment but... it depends on actor if has DT_Sprite or DT_Mesh or it's hidden or not hidden - look at replication and figure how an actor should look like for being shown/replicated properly but without to exhaust network channels.

Next:
A trigger can be 40 × 40, 40 × 120, 100 × 512 - even 512 × 256, 256 × 40, etc. - light me how to draw a mesh for such case...
User avatar
Feralidragon
Godlike
Posts: 5489
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: in-game radii view for triggers

Post by Feralidragon »

To be honest, I am not sure why you cannot see the trigger references either.
The array is being replicated, and at the time of replication the triggers should be relevant to the client, so there's seemingly no reason for the references to not be replicated.

Maybe it's the size of the array itself, since it seems the engine has limitations in replicating large arrays:
http://web.archive.org/web/200608132205 ... ation.html
Arrays of variables can be replicated, but only of the size of the array (in bytes) is less than 448 bytes.
But as far as I remember I think I replicate larger arrays than this in NW3 alone at times, unless it's 448 bytes per "change" (even if I have a larger array, if only a subset changes and is less than 448 bytes it's replicated, otherwise isn't?), so I am not sure about the veracity of this statement, and structs are either replicated whole or not at all, so if it was some sort of limit, then you would either see all references, or they would all be None/zeroed.

Either way, try first to limit your array to only 8 elements for example, and see what happens.

Or maybe the engine is choosing to not replicate the triggers themselves right away when that array is replicated, resulting in None references, and maybe only replicates them later on, which would be weird in this case, but not impossible (I have seen similar things happening which I had to account for before).

Also, in a slightly separate note, rather than hardcoding the number of elements in an array in the for (i = 0; i < 10000; i++) statements, you can instead use for (i = 0; i < ArrayCount(TS); i++).
The compiled code in the end will result in exactly the same thing, since ArrayCount is essentially a macro that results in a constant value at compile time, but that way you are free to change the size of an array without having to change the rest of the code that depends on it.
OwYeaW
Experienced
Posts: 81
Joined: Fri Jan 09, 2015 4:24 pm

Re: in-game radii view for triggers

Post by OwYeaW »

sektor2111 i dont really understand what you are trying to tell, can you give a more exact explanation?

thanks for the tip about ArrayCount FeraliDragon
so, i tested with only 8 elements but it didnt make any difference

i have been doing a lot of experiments and trying to re-code stuff but i still didnt get it working properly

i found out that when an actor is bStatic it will always be shown in the array to the client, even if the actor is bHidden
(bStatic is a constant variable so i had to test this by changing the value inside the map itself)
of course bStatic is not an option because the cylinders need to be able to move with the Actor(i now included teleporters and flags too, so not only triggers)

another thing i found out is that sometimes the Actors can be seen within the array, but only those that were in sight when the player joined the server
very weird: https://streamable.com/3ncso (im reconnecting many times until it somehow "works")

is there anyone that can give an explanation to this?
how can i make sure the client always sees the content of the array?
in case you want to see the classes: https://github.com/OwYeaW/ActorCollisionStuff
User avatar
Feralidragon
Godlike
Posts: 5489
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: in-game radii view for triggers

Post by Feralidragon »

Actors with bStatic are always relevant, it has the same effect as bAlwaysRelevant in terms of network relevancy alone.

As for the behavior you're seeing, now it starts to make sense to me.

The first thing you have to be aware of is that actors are only replicated if they are relevant to the client, which of course extends to actor references, such as the actor references in your array, meaning that if the actors you're setting there are not relevant at that point to the client, the references are not going to be replicated, because the actors themselves are not relevant and are not going to be replicated either.

The second thing is that replication only occurs when variables change, be it the first time from their default values, be it in subsequent ticks when they differ from the value in the previous tick.
This means that once replication occurs, unless the variable changes again, it's not replicated to the same client again.
It's replicated to new clients (new connections) however since its current value differs from its default value, which is why you see different triggers replicated when you reconnect.

The third thing is that, whenever an actor stops being relevant to a client, the client destroys it on its side, despite still existing in the server.
This means that any references to that actor are also deleted in the client, hence the references returning to None, and due to the second thing I mentioned above, while the actor is replicated again once it becomes relevant, the reference in the array isn't because it didn't change from its previous local value.

(hopefully I am not saying anything stupid here, it's been a while since I looked at any of this, so if anyone sees a mistake here please correct me)

All of this to say that replicating the actors themselves is not something that will help you, even because all things considered, you don't really need an array for that if you're going to use actor references anyway.

So what you do is drop the actor reference altogether, and just keep what you need, such as location, textures, etc, also meaning that you should no longer set these actors (triggers) to be visible.
And on the Spawned event, you initialize everything like you are already doing, and then using the Tick event you update the locations of the triggers in the array over time.

Of course, for this you are going to need a way to keep a mapping between the array entries you're updating and the actors you're updating them from.
For this, you can create another array, of the same size, with actors only, and not replicated at all, and during the Spawned event you populate this array at the same time as the other array, so that at the end each position in one array corresponds to the other.

Then in the Tick event, you just iterate the local actors array, and update the locations in the other array (the one to be replicated) directly since the positions are exactly the same.

If you want to optimize all of this even further, you can have an array with just the textures and other stuff you only need to initialize once, and have the locations in yet another separate array so that whenever a location changes, only that location is replicated rather than a full struct composed of location, textures, etc.

From here, there are other minor issues you will need to solve, but with this working everything will be easier from here, but one step at a time (this post is huge as it is).
OwYeaW
Experienced
Posts: 81
Joined: Fri Jan 09, 2015 4:24 pm

Re: in-game radii view for triggers

Post by OwYeaW »

alright, after enjoying some holidays i started working on this again

seems like a good idea Feralidragon, i got it fully coded, although there seems to be a problem; is there a "replication cap" ?

because the client can only see the values from the array until number 256
also any other extra variable from the class will not be replicated to the client anymore
so it seems like the array takes up all the resources?

i need at least a few thousand replicated
User avatar
Feralidragon
Godlike
Posts: 5489
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: in-game radii view for triggers

Post by Feralidragon »

The only cap that I know of is from this quote:
Arrays of variables can be replicated, but only of the size of the array (in bytes) is less than 448 bytes.
But to be honest, at 256 entries, you already surpassed these 448 bytes a long time ago, and in NW3 for example, I have replication of several different kinds of big arrays (generally up to 64 entries each, all in the same classes), where each element occupies a rather large memory footprint (relatively speaking), so this statement is likely to be wrong, or maybe it's only applied to older engine versions (v432 and below?).

Either way, I had problems with replication limitations in the past, but it was only due to the number of actors with open replication channels, but never concerning the amount of data itself.

People like @Higor and @anth may be able to shed some more light onto this matter (which I would like to know too).
If there's some sort of hard limitation, this could be also something to be taken into account in the v469 patch.
OwYeaW
Experienced
Posts: 81
Joined: Fri Jan 09, 2015 4:24 pm

Re: in-game radii view for triggers

Post by OwYeaW »

would indeed be nice to know about this possible replication limitation per actor

i managed to get "everything" working now by spawning another actor that collects the remaining data once the array of 256 entries is filled
basically spawning a brother to continue further with collecting+replicating the data

also; i couldnt update the location via replication since the limitation, so i took a different route here

basically what i did is check if the actor is attached to "something", then replicate the reference to this "something"
then i let the spawned collisionactor follow this "something" via the Tick function in its own class (client sided since its only spawned on the client)

sadly this method doesnt work in all cases, for example when an actor is attached to a monster, the reference to the monster cant be replicated
but this is a really small exception which ive only seen in one of my own maps yet (i have teleporters attached to monsters there)

so basically in 99.99% of the cases the actor its attached to is a mover, and this can always be replicated
so im kindof satisfied with this, im already going way too deep with this lol
Post Reply