This event has been something of a mystery- we've confirmed that it fires after the OnEnter() event- it seems to act as a way to circumvent some of the shortcomings of OnEnter (which tends to run while the player/DM client is still loading the area, meaning oPC isn't available for some scripted operations).
The question is, what event returns the entering PC for the Area OnClientEnter event? Certainly, we can use the GetEnteringObject() function that we use for OnEnter, and this works in some cases, but I've also seen some evidence that this isn't robust or reliable when you have several PCs ATing together into an area. It seem to me, GetEnteringObject() is going to return the most recent OnEnter target, which may well be different from the one who triggered the OnClientEnter. I haven't seen any function that looks like a reasonable analog to GetEnteringObject (GetEnteringClient?), and I believe OBJECT_SELF continues to be oArea. The NWN1 lexicon isn't any help in this, as the event didn't exist in NWN1, and I've not seen any postings on the subject in the NWN2 forums or elsewhere.
Any ideas?
Area On Client Enter
Moderators: ALFA Administrators, Staff - Technical
- AcadiusLost
- Chosen of Forumamus, God of Forums
- Posts: 5061
- Joined: Tue Oct 19, 2004 8:38 am
- Location: Montara, CA [GMT -8]
- Contact:
Here's an order of events. Not much more than that.
And this the OnEnter:
But here are some example scripts from Rowell's module that may be more useful. This is the OnClientEnter:Event sequence as far as I can tell from logs this far during client login process.
1. AquireItem Events fire for all items the logging in PC carries.
2. Equip Item Events fire for all items the PC has equipped.
3. Client Enter Event Fires
4. UnEquip event fires for anything forcibly unequipped during the Client Enter Script (or due to ELC/ILR restrictions too I think)
5. UnAcquire Item events fire for anything removed during client Enter.
6. PC Loaded Event fires.
7. Area On Enter event fires
8. Area On Client Enter event fires.
Code: Select all
// =============================================================
//
// File: row_area_enter
// Desc: Rowell's Player Enters Area Script
// Author: Michael Marzilli
// Site: http://www.engliton.org
//
// Created: Nov 06, 2006
// Updated: Nov 06, 2006
// Version: 1.0.0
//
// Usage: Place this script in the [b]OnClientEnter[/b] Event in all Areas
//
// =============================================================
#include "row_inc_functions"
void main() {
object oPC = GetEnteringObject();
if (GetIsPC(oPC)) {
if (GetTag(GetArea(oPC)) == GetTag(GetAreaFromLocation(GetStartingLocation())))
Row_ModulePlayerLoaded(oPC);
else
Row_AreaEnter(oPC);
// USE CUSTOM LIGHTING SYSTEM - (IE TURN LAMPS/TORCHS ON/OFF DURING THE NIGHT/DAY)
if (GetLocalInt(OBJECT_SELF, "USE_LIGHTING"))
ExecuteScript("row_area_enter_light", OBJECT_SELF);
}
// INSERT YOUR OWN CODE BELOW
// ===================================================================================
}
Code: Select all
// =============================================================
//
// File: row_area_enter_lights
// Desc: Rowell's Player Enters Area Script - Turn On/Off Lights
// Author: Michael Marzilli
// Site: http://www.engliton.org
//
// Based On the work of Puget (puk_lights; Jan 13, 2007)
//
// Created: Feb 06, 2007
// Updated: Feb 06, 2007
// Version: 1.0.0
//
// Usage: Place this script is called from the row_area_enter script
// You will need to add and set up the following:
// o 1 Placeable Object that is the Source of the light (ie a Torch, Lampost, Candle, etc)
// o 1 Light Placeable Object just above where the flame/light would be coming from that object.
//
// 1) Move the Light Placeable Object just over the Placeable, where the flame/light would be.
// 2) Change the Tag of this Light Object to: "row_light"
// 3) Add a new Local Variable onto the Light Placeable Object called "FX_SEF".
// 4) Make the FX_SEF variable a String type, and put in ONE of the following:
// fx_torchglow, fx_lampglow, fx_candle
//
// That should do it. Lights should turn on/off depending on the time of day whenever a player enters the area.
//
// =============================================================
void TurnLightOn(object oLight) {
string sFX = GetLocalString(oLight, "FX_SEF"); //the fx file (fx_lampglow, fx_torchglow, fx_candle)
effect eLight = EffectVisualEffect(VFX_DUR_LIGHT_YELLOW_20);
effect eFX = EffectNWN2SpecialEffectFile(sFX);
DelayCommand(0.4, SetPlaceableIllumination(oLight, TRUE));
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLight, oLight);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eFX, oLight);
}
void TurnLightOff(object oLight) {
string sFX = GetLocalString(oLight, "FX_SEF"); //the fx file (fx_lampglow, fx_torchglow, fx_candle)
DelayCommand(0.4, SetPlaceableIllumination(oLight, FALSE));
RemoveSEFFromObject(oLight, sFX);
effect eEffect = GetFirstEffect(oLight);
while (GetIsEffectValid(eEffect) == TRUE) {
if (GetEffectType(eEffect) == EFFECT_TYPE_VISUALEFFECT)
RemoveEffect(oLight, eEffect);
eEffect = GetNextEffect(oLight);
}
}
void main() {
object oArea = OBJECT_SELF;
object oLight;
int iIsNightTime;
// MAKE SURE THE oArea OBJECT IS INDEED AN AREA
if (GetObjectType(oArea) == OBJECT_TYPE_PLACEABLE) {
// THIS SECTION IS MAINLY FOR DEBUGGING PURPOSES
// WHEN THIS SCRIPT IS CALLED FROM THE OnUsed EVENT OF A PLACEABLE,
// IT WILL TOGGLE ALL THE row_light LIGHTS IN THE AREA ON/OFF
oArea = GetArea(oArea);
iIsNightTime = (!GetLocalInt(oArea, "ROW_LIGHT_ON"));
} else {
// FIND OUT THE TIME OF DAY IT IS
if ( GetIsNight() || GetIsDusk() || !GetIsAreaAboveGround(oArea) || GetIsAreaInterior(oArea) )
iIsNightTime = TRUE;
else
iIsNightTime = FALSE;
}
// CHECK THE OBJECTS IN THE AREA FOR LIGHTS
if (iIsNightTime != GetLocalInt(oArea, "ROW_LIGHT_ON")) {
oLight = GetFirstObjectInArea(oArea);
while (oLight != OBJECT_INVALID) {
// ONLY PROCESS THE LIGHT WAYPOINT OBJECTS
if (GetObjectType(oLight) == OBJECT_TYPE_PLACEABLE && GetTag(oLight) == "row_light") {
// ONLY PROCESS THE LIGHTS WHEN THEY NEED TO BE TURNED ON / OFF
if (iIsNightTime != GetLocalInt(oLight, "ROW_LIGHT_ON")) {
// TURN THE LIGHTS ON/OFF
if (iIsNightTime)
TurnLightOn(oLight);
else
TurnLightOff(oLight);
// STORE THE LIGHT'S SETTING IN A LOCAL VARIABLE
SetLocalInt(oLight, "ROW_LIGHT_ON", iIsNightTime);
}
}
// GET THE NEXT OBJECT IN THE AREA
oLight = GetNextObjectInArea(oArea);
}
SetLocalInt(oArea, "ROW_LIGHT_ON", iIsNightTime);
}
// RECOMPUTE THE LIGHTING FOR THE AREA
DelayCommand(1.0, RecomputeStaticLighting(oArea));
// INSERT YOUR OWN CODE BELOW
// ===================================================================================
}

- AcadiusLost
- Chosen of Forumamus, God of Forums
- Posts: 5061
- Joined: Tue Oct 19, 2004 8:38 am
- Location: Montara, CA [GMT -8]
- Contact:
From those examples, it seems at least they are still using GetEnteringObject() from the Area's OnClientEnter() events. It is possible that that function is reliable for such use (meaning there is some other explanation for the inconsistent behavior I've seen), it's also possible that the authors of that script didn't stringently test two PCs ATing into an area together at the same time (but with computers that run different speeds). Unfortunately, this case will come up frequently in a PW where PCs travel together.
Imagine client A enters the AT a split-second before client B.
1. OnAreaEnter() is triggered for PC A. (GetEnteringObject=oPC_A)
2. OnAreaEnter() is triggered for PC B. (GetEnteringObject=oPC_B)
3. OnClientEnter() is triggered by PC A as the area finishes loading (GetEnteringObject still = oPC_B)
4. OnClientEnter() is triggered by PC B (GetEnteringObject = oPC_B)
I suspect this sort of condition is tied into the option to pause the server during area transitions (which I think is an option configuarable in the .ini files)
Imagine client A enters the AT a split-second before client B.
1. OnAreaEnter() is triggered for PC A. (GetEnteringObject=oPC_A)
2. OnAreaEnter() is triggered for PC B. (GetEnteringObject=oPC_B)
3. OnClientEnter() is triggered by PC A as the area finishes loading (GetEnteringObject still = oPC_B)
4. OnClientEnter() is triggered by PC B (GetEnteringObject = oPC_B)
I suspect this sort of condition is tied into the option to pause the server during area transitions (which I think is an option configuarable in the .ini files)
- ç i p h é r
- Retired
- Posts: 2904
- Joined: Fri Oct 21, 2005 4:12 pm
- Location: US Central (GMT - 6)
GetEnteringObject() is all that we have available that I'm aware of, so I think we have to assume that it correctly references the last object which successfully transitioned into the area. Being single threaded, there's no possibility that two things can happen simultaneously but there could certainly be a bug which causes the wrong return value. This is new behavior after all so it's at least a possibility.
Do you have a specific need for this function?
Do you have a specific need for this function?
- AcadiusLost
- Chosen of Forumamus, God of Forums
- Posts: 5061
- Joined: Tue Oct 19, 2004 8:38 am
- Location: Montara, CA [GMT -8]
- Contact:
Since OnEnter fires as soon as the PC starts to transition to a new area (while the player client is working on loading the area), it stands to reason that anyone else attempting to AT into that area during the first PC's loading time (which can be significant depending on the area size and the speed of the client computer) will become the new EnteringObject for the area, before the OnClientEnter() is triggered by the first PC. Does that explanation make sense?
This has consequences for a number of seamless AT functions, as well as things like PC location saves, which are refreshed OnClientEnter for GetEnteringObject() - so you might be travelling with a group and get the same PC (last one to enter the area) being the target of all the ACR_SavePC() calls.
This has consequences for a number of seamless AT functions, as well as things like PC location saves, which are refreshed OnClientEnter for GetEnteringObject() - so you might be travelling with a group and get the same PC (last one to enter the area) being the target of all the ACR_SavePC() calls.
- ç i p h é r
- Retired
- Posts: 2904
- Joined: Fri Oct 21, 2005 4:12 pm
- Location: US Central (GMT - 6)
It makes sense, but it's not necessarily true. There's obviously another event trigger for when the transition completes, which presumably records the object that triggered the event as well. If it doesn't, there's the bug.AcadiusLost wrote:Since OnEnter fires as soon as the PC starts to transition to a new area (while the player client is working on loading the area), it stands to reason that anyone else attempting to AT into that area during the first PC's loading time (which can be significant depending on the area size and the speed of the client computer) will become the new EnteringObject for the area, before the OnClientEnter() is triggered by the first PC. Does that explanation make sense?
It's a single threaded game, so only one event can be processed at a time. And that means the object/event relationship can never be compromised by a race condition.