View previous topic :: View next topic |
Author |
Message |
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Wed Jan 03, 2018 23:30 Post subject: Maxrock - nwnx_funcs and nwnx_areas source code request pls |
|
|
Hi Guys,
I've been recently looking to try and diagnose the crashes that kinda drove me out of hosting a nwn server.
I use Amazon AWS for all my hosting needs - including game servers- I suspect it might be a strange interaction with the virtualisation technology or something or other...
In any case- all the nwnx plugins I have work perfectly on AWS when the operating system is Windows 2003 - which is now kinda discontinued - rightly so, security flaws etc.
This was also a 32bit operating system.
I am now hosting all my services, websites etc off a Windows server 2012 virtual box - which is 64bit and hypervirtualized (in case that makes any difference)
After alot of shuffling and deleting of plugins and the like.
I believe I have found the cause of my crashes - now comes the intricate task of fixing it - however, I have found that the two plugins which are suspect, I cannot find source code for them anywhere..
nwnx_areas - by Maxrock (I know Virusman did the original, but I am not sure if the Maxrock fixes were ever edited in)
and
nwnx_funcs by Maxrock.
The crashes always occur when
SetTag is called from funcs
or
SetAreaName is called from areas
Perhaps its a coincidink, but it occurs to me that if there is some sort of memory mismanagement / 64bit querk going on, it stands to reason that the author of both plugins may have used the same memory allocation code in both plugins - resulting in the crashes I am seeing.
Eg: It happens when trying to set a tag and set an area name - both deal with setting strings on objects - perhaps its a reach?
For those who are interested in the crash details:
Faulting application name: nwserver.exe, version: 1.6.9.0, time stamp: 0x486cfaee
Faulting module name: ntdll.dll, version: 6.3.9600.18821, time stamp: 0x59ba8666
Exception code: 0xc0000005
Fault offset: 0x00054995
Faulting process id: 0x1724
Faulting application start time: 0x01d384d82a7f3d03
Faulting application path: E:\Servers\NWN\nwserver.exe
Faulting module path: C:\Windows\SYSTEM32\ntdll.dll
Report Id: bf0ff1bb-f0cb-11e7-8178-0e754580076f
Faulting package full name:
Faulting package-relative application ID:
Not particularly helpful - but my research points to it being a memory violation - accessing memory the application shouldnt be trying to access etc.
I will post this over at nwnx.org too - but if anyone can assist - by all means. |
|
Back to top |
|
|
DarkSet
Joined: 06 Jun 2016 Posts: 98
|
Posted: Thu Jan 04, 2018 0:07 Post subject: |
|
|
I use both this plugins. SetTag crashes as well, but SetAreaName works fine.
I use Win 8.1 x64, no virtuaization. But My test env is VirtualBox WinXP and all works the same there. |
|
Back to top |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jan 04, 2018 0:27 Post subject: |
|
|
I think I know the cause
Looking at the SetTag code that Maxrock was using - I dont think it takes into consideration setting of tags that are bigger than the original.
It results in trying to squeeze say 16 bytes of data into a variable space declared for 10 bytes - hence the memory violation.
Im more a java / C# developer
So trying to solve this C++ problem might be beyond my skills.
Looking at more recent code by terrahkitsune -
He is using a custom malloc function to handle the nwn memory.
I am wondering if malloc could be slotted in just to fix this one issue though? |
|
Back to top |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jan 04, 2018 0:39 Post subject: |
|
|
Im testing nwnx_mem from this url
https://github.com/TerrahKitsune/NWNX/releases
It was designed to hook the memory allocation and deallocation to try and resolve these issues.
I will report back my status |
|
Back to top |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jan 04, 2018 3:37 Post subject: |
|
|
Ok... No luck with the crash - although, through debug strings, I have isolated the scenario that triggers it for me.
When generating a new area via nwnx_areas - I find a door object in the area and then I set its tag to a new value - to create instanced areas.
It seems to crash at this point:
Quote: | [01/04/2018 01:33:06] - StrReq: "SETTAG" Params: "vamp_castle_5"
[01/04/2018 01:33:06] o Setting Tag vamp_castle_5
[01/04/2018 01:33:06] o Object found vamp_castle_5
[01/04/2018 01:33:06] o Object type is 10
[01/04/2018 01:33:06] o Constructed sNewTag vamp_castle_5
[01/04/2018 01:33:06] o Remove from lookup vamp_castle_5
[01/04/2018 01:33:06] o Add to lookup vamp_castle_5
[01/04/2018 01:33:06] o Setting Tag vamp_castle_5
[01/04/2018 01:33:06] o Finished Setting Area Tag
[01/04/2018 01:33:06] - StrReq: "SETTAG" Params: "vamp_castle_20"
[01/04/2018 01:33:06] o Setting Tag vamp_castle_20
[01/04/2018 01:33:06] o Object found vamp_castle_20
[01/04/2018 01:33:06] o Object type is 10
[01/04/2018 01:33:06] o Constructed sNewTag vamp_castle_20
[01/04/2018 01:33:06] o Remove from lookup vamp_castle_20
[01/04/2018 01:33:06] o Add to lookup vamp_castle_20
[01/04/2018 01:33:06] o Setting Tag vamp_castle_20
|
It always seems to be the vamp_castle_20 one that it crashes on.
I am wondering if there is a length limit or something? |
|
Back to top |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jan 04, 2018 4:01 Post subject: |
|
|
Yay - I got it working
So it wasnt anything to do with area tags per say, it was something weird with the way it was setting the door tags
My guess was that it was as I said - trying to squeeze 14 chars into a 13 char variable.
The original tag size was: 13
vamp_port_loc
But it would allow:
vamp_castle_5
but not allow
vamp_castle_20
I fixed it by using some of Terrahkitsue's code - I then made it use the CExoStringCpy method:
obj->obj_tag.CExoStringCpy(Params);
This method is designed to account for resizing of the CExoStrings
After I got this resolved- I had a crash that was occuring on login.
I resolved it by updating my nwnx_chat plugin to be terrahkitsunes as it I believe is 64bit friendly and uses the enhanced memory management. |
|
Back to top |
|
|
DarkSet
Joined: 06 Jun 2016 Posts: 98
|
Posted: Thu Jan 04, 2018 16:57 Post subject: |
|
|
so SetTag is working for you now? Or this is setting the target of a door tag?
BTW, do you have a java plugin for nwnx? As you'r a java/C# guy. I want to use it but can't find one. |
|
Back to top |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jan 04, 2018 17:41 Post subject: |
|
|
I have now got a functional nwnx_funcs - that has a set tag that works without crashing - touch wood.
As for the nwnx plugin for java - I think it only exists for linux at the moment.
It is part of the main nwnx github page.
https://github.com/NWNX/nwnx2-linux
I do have a dotnet plugin - If you want that, I will need to look out the sources - they got a little muddled.
The dotnet and java plugins are good - cause they let you build virtual programming environments off to the side of your live server- and carry out parallel tasks in that programming environment which can then be checked within the nwn game universe.
Eg: Lets imagine you want to have an 'energy' level for players- you can have a thread off to the side that recharges their energy- without having to do pseudoloops in nwn. |
|
Back to top |
|
|
DarkSet
Joined: 06 Jun 2016 Posts: 98
|
Posted: Fri Jan 05, 2018 2:16 Post subject: |
|
|
well, if it's not too hard for you to find and publish that dotnet plugin - it would be great. And if you have some docs for it as well - even better.
I still prefer java, but c# is for sure better than nwscript
Oh and I remembered - Shadooww has set tag function in his nwnx_patch working fine. It does not set tags of areas thou, but as he explained there is a difference between all the tags and area tags. |
|
Back to top |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Fri Jan 05, 2018 2:55 Post subject: |
|
|
https://github.com/zunath/NWNXDotNet_Modified
Zunath put it on github a while back - a clone of the one I was working on.
It should be noted - for the C++ dll to build, the .Net Reflector dll needs to be present.
Then, the C++ dll and the Reflector dll need to be present in your nwn directory.
You can then compile C# code or even just supply text files that contain the class definitions - it allows for runtime compilation - so you can fix your scripts as the server is running.
As for the SetTag thing - I did look at his SetTag function- there wasnt anything fundamentally different between his and Maxrocks method of setting a tag - I think it was the lower level process of how it was overritting the existing tag.
Maxrocks on Server 2008+ 64 seemed to get an overflow or access violation - my guess from the incorrect manipulation of memory.
(Its possible something in windows changed...)
I copied the 2 classes from TerrahKitsune's project - nwnx_memory and took his implementation of CExoString.cpp
Now instead of doing tag = newValue;
I do tag.CExoStringCpy(newValue);
The method takes care of string resizing etc |
|
Back to top |
|
|
DarkSet
Joined: 06 Jun 2016 Posts: 98
|
Posted: Sun Jan 07, 2018 5:29 Post subject: |
|
|
well, thanks. But there is no manual, only sources |
|
Back to top |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Tue Jan 09, 2018 13:34 Post subject: |
|
|
Code: | #include "dotnetplugin_inc"
//////////////////////////////
///// PROTOTIPES /////////////
//////////////////////////////
//Effectively deletes the last saved char from the specified player.
//It will only delete the character vault if it is marked to deletion.
//PLEASE NOTE the BootPlayerForVaultDeletion() function should be called before this,
// function, or it will not work.
//PLEASE NOTE the best local to put this function is on OnClientLeave module event.
void CheckAndDeleteLastServerVault();
//Starts the character deletion. It will boot the PC and mark some variables to effective
// character deletion when the player is exiting.
//PLEASE NOTE you SHOULD implement DeleteLastServerVault() on OnClientLeave module event,
// or it will not work
//Parameters:
// oPlayerCaller: the player that is calling the method. Use this to check if the calling
// player have rights to do it (you should implement your own rules)
// oDeleteFromPlayer: the player being his char deleted
void BootPlayerForVaultDeletion(object oPlayerCaller, object oDeleteFromPlayer);
//Returns the REAL current server date.
//Parameters:
// Format: formats the return.
string GetDate(string Format="yyyy/MM/dd HH:mm:ss");
//Returns a GUID (Global Unique Identifer), string which you can
// use as a PrimaryKey. A GUID is supposed to never repeat
string GetGuid();
string RunDLLMethod(string sDLL, string sNameSpace, string sClassName, string sMethod, string sArgs);
//////////////////////////////
///// FUNCTIONS //////////////
//////////////////////////////
void CheckAndDeleteLastServerVault() {
object oPC = GetExitingObject();
string strPlayerCaller = GetLocalString(oPC, "DOTNET_DELETEVAULT_CALLING_PLAYER");
if (strPlayerCaller != "") {
string strDeleteFromPlayer = GetLocalString(oPC, "DOTNET_DELETEVAULT_DELETING_PLAYER");
DotNetExecute("DeleteLastServerVault", strPlayerCaller, strDeleteFromPlayer, "utc");
}
}
void BootPlayerForVaultDeletion(object oPlayerCaller, object oDeleteFromPlayer) {
SetLocalString(oDeleteFromPlayer, "DOTNET_DELETEVAULT_CALLING_PLAYER", GetPCPlayerName(oPlayerCaller));
SetLocalString(oDeleteFromPlayer, "DOTNET_DELETEVAULT_DELETING_PLAYER", GetPCPlayerName(oDeleteFromPlayer));
DelayCommand(0.2, FloatingTextStringOnCreature("You will be booted for character deletion (" + GetPCPlayerName(oDeleteFromPlayer) + ")", oDeleteFromPlayer));
DelayCommand(2.5, BootPC(oDeleteFromPlayer));
}
string GetDate(string Format="yyyy/MM/dd HH:mm:ss") {
return DotNetExecute("GetDate", "yyyy/MM/dd");
}
string GetGuid() {
return DotNetExecute("GetGuid");
}
string SendMail(string sAddress, string sSubject, string sContent, string sFromAddress, string sFromName)
{
//SendMail(string sAddress, string sSubject, string sContent, string sFromAddress, string sFromName)
return DotNetExecute("SendMail", sAddress, sSubject, sContent, sFromAddress, sFromName);
}
string RunClassMethod(string sClassFile, string sMethodName, string sArgs)
{
return DotNetExecute("ExecuteClassCode", sClassFile, sMethodName, sArgs);
}
string RunDLLMethod(string sDLL, string sNameSpace, string sClassName, string sMethod, string sArgs)
{
return DotNetExecute("ExecuteAssembly", sDLL, sNameSpace, sClassName, sMethod, sArgs);
}
//void main() {} |
and
Code: | //Executes methods and paramenters agains the DotNet Plugin
//The methods and parameters MUST exist in the Exection class of that Plugin
//The buffer size should be adjusted to fit the size of your expected return
//(if you inform a buffer lesser than expected results, it will be truncated)
//(parameters informed with {&qt} are ignored, once that string is reserved for the function)
string DotNetExecute(string MethodName, string Param1="{&qt}", string Param2="{&qt}", string Param3="{&qt}", string Param4="{&qt}", string Param5="{&qt}", string Param6="{&qt}", string Param7="{&qt}", string Param8="{&qt}", string Param9="{&qt}", string Param10="{&qt}", int BufferSize=500);
//Get the filler string based in the informed length.
string DotNetGetFiller(int Length);
//Encode quotes
string DotNetEncodeQuote(string sString);
//DotNetExecute("ExecuteClassCode", sClassFile, sMethodName, sArgs);
string DotNetExecute(string MethodName, string Param1="{&qt}", string Param2="{&qt}", string Param3="{&qt}", string Param4="{&qt}", string Param5="{&qt}", string Param6="{&qt}", string Param7="{&qt}", string Param8="{&qt}", string Param9="{&qt}", string Param10="{&qt}", int BufferSize=500) {
//encode quotes
//first, check it
string strQuote = "{&qt}";
int blnError = FALSE;
if (Param1 != "{&qt}" && FindSubString(Param1, strQuote) >-1 ) { blnError = TRUE; }
if (Param2 != "{&qt}" && FindSubString(Param2, strQuote) >-1 ) { blnError = TRUE; }
if (Param3 != "{&qt}" && FindSubString(Param3, strQuote) >-1 ) { blnError = TRUE; }
if (Param4 != "{&qt}" && FindSubString(Param4, strQuote) >-1 ) { blnError = TRUE; }
if (Param5 != "{&qt}" && FindSubString(Param5, strQuote) >-1 ) { blnError = TRUE; }
if (Param6 != "{&qt}" && FindSubString(Param6, strQuote) >-1 ) { blnError = TRUE; }
if (Param7 != "{&qt}" && FindSubString(Param7, strQuote) >-1 ) { blnError = TRUE; }
if (Param8 != "{&qt}" && FindSubString(Param8, strQuote) >-1 ) { blnError = TRUE; }
if (Param9 != "{&qt}" && FindSubString(Param9, strQuote) >-1 ) { blnError = TRUE; }
if (Param10 != "{&qt}" && FindSubString(Param10, strQuote) >-1 ) { blnError = TRUE; }
if (blnError) {
return "(error)Don't use the substring {&qt}, it is reserved";
}
//now, replace
if (Param1 != "{&qt}" && FindSubString(Param1, "'") >-1 ) { Param1 = DotNetEncodeQuote(Param1); }
if (Param2 != "{&qt}" && FindSubString(Param2, "'") >-1 ) { Param2 = DotNetEncodeQuote(Param2); }
if (Param3 != "{&qt}" && FindSubString(Param3, "'") >-1 ) { Param3 = DotNetEncodeQuote(Param3); }
if (Param4 != "{&qt}" && FindSubString(Param4, "'") >-1 ) { Param4 = DotNetEncodeQuote(Param4); }
if (Param5 != "{&qt}" && FindSubString(Param5, "'") >-1 ) { Param5 = DotNetEncodeQuote(Param5); }
if (Param6 != "{&qt}" && FindSubString(Param6, "'") >-1 ) { Param6 = DotNetEncodeQuote(Param6); }
if (Param7 != "{&qt}" && FindSubString(Param7, "'") >-1 ) { Param7 = DotNetEncodeQuote(Param7); }
if (Param8 != "{&qt}" && FindSubString(Param8, "'") >-1 ) { Param8 = DotNetEncodeQuote(Param8); }
if (Param9 != "{&qt}" && FindSubString(Param9, "'") >-1 ) { Param9 = DotNetEncodeQuote(Param9); }
if (Param10 != "{&qt}" && FindSubString(Param10, "'") >-1 ) { Param10 = DotNetEncodeQuote(Param10); }
//get parameter count
int intParamCount = 0;
if (Param1 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param2 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param3 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param4 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param5 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param6 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param7 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param8 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param9 != "{&qt}") { intParamCount = intParamCount + 1; }
if (Param10 != "{&qt}") { intParamCount = intParamCount + 1; }
//build request string
string strRequest = "";
strRequest = "-m:'" + MethodName + "' -pc:'" + IntToString(intParamCount) + "' -p1:'" + Param1 + "'" + " -p2:'" + Param2 + "'" + " -p3:'" + Param3 + "'" + " -p4:'" + Param4 + "'" + " -p5:'" + Param5 + "'" + " -p6:'" + Param6 + "'" + " -p7:'" + Param7 + "'" + " -p8:'" + Param8 + "'" + " -p9:'" + Param9 + "'" + " -p10:'" + Param10 + "' ";
//PrintString(strRequest);
//set filler
if (GetStringLength(strRequest) < BufferSize) {
strRequest = strRequest + DotNetGetFiller(BufferSize-GetStringLength(strRequest));
}
//execute
SetLocalString(GetModule(), "NWNX!DOTNETPLUGIN!EXECUTE", strRequest);
//return
return GetLocalString(GetModule(), "NWNX!DOTNETPLUGIN!EXECUTE");
}
string DotNetGetFiller(int Length) {
if (Length > 0) {
int i = 0;
string strReturn = "....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................";
while (GetStringLength(strReturn) < Length) {
strReturn = strReturn + strReturn;
i++;
if (i>50) break;
}
return GetSubString(strReturn, 0, Length);
}
else {
return "";
}
}
string DotNetEncodeQuote(string sString)
{
if (FindSubString(sString, "'") == -1) // not found
return sString;
int i;
string sReturn = "";
string sChar;
// Loop over every character and replace special characters
for (i = 0; i < GetStringLength(sString); i++)
{
sChar = GetSubString(sString, i, 1);
if (sChar == "'")
sReturn += "{&qt}";
else
sReturn += sChar;
}
return sReturn;
} |
These two methods are the stars of the show:
Code: | string RunClassMethod(string sClassFile, string sMethodName, string sArgs)
{
return DotNetExecute("ExecuteClassCode", sClassFile, sMethodName, sArgs);
}
string RunDLLMethod(string sDLL, string sNameSpace, string sClassName, string sMethod, string sArgs)
{
return DotNetExecute("ExecuteAssembly", sDLL, sNameSpace, sClassName, sMethod, sArgs);
} |
Run DLL method will look inside the CSharpClasses directory (in your nwn directory) - and find the DLL that matches the name, then it will find the class within the namespace, and execute the method with given arguments and return the output back to the NWScript.
The RunClassCode does much the same - except it will actually compile the C# Code when the method is called -
An example of sending an email via the RunClassCode:
RunClassMethod("EMailServices", "SendMailNonBlocking", sEmail+"A Prayer from "+sFrom+" in Rhun"+sMessageFinal+"noReply@azmodan.net Prayers from Rhun");
AssignCommand(oPC,SpeakString("Prayer Sent:"+sMessage));
If im not mistaken, the arguments being fed to the assembly, need to be separated by the ¬ character.
The RunClassCode is good if you want to test code, before compiling it.
Although, if you compile it to a dll, and execute it from there, you will get obvious performance increase - because the compilation is not having to happen everytime the code is executed.
Note - once you use a DLL method, the DLL is loaded into the server and cannot be unloaded until you restart the server. |
|
Back to top |
|
|
Zunath
Joined: 06 Jul 2006 Posts: 183
|
Posted: Fri Jan 12, 2018 18:19 Post subject: |
|
|
The Java plugin is honestly the one you want to use. The C# doesn't have nearly the same level of support that the Java one does.
I've been working with Niv on getting the Java plugin ported to NWN: EE. Still working on integration, but it's going fairly well. C# would be my preference but Java is just as good.
It -is- Linux only, but with the new version you can host using docker containers and it's a heck of a lot easier.
Here's my server code (all in Java/SQL): https://github.com/zunath/CyberpunkZombieSurvival_JVM
Hopefully it'll give you some ideas. |
|
Back to top |
|
|
DarkSet
Joined: 06 Jun 2016 Posts: 98
|
Posted: Mon Jan 15, 2018 20:14 Post subject: |
|
|
Well, thanks, but Linux is not what I'm going to invest my time now. Maybe NWN:EE will provide windows server with java plugin supported. At least this could explaine why they want 40$ for 5$ game |
|
Back to top |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Tue Jan 16, 2018 17:09 Post subject: |
|
|
The .net one may not have as much access to the NWN virtual machine / script methods - but giving it access is not impossible.
https://github.com/Baaleos/NWNDotNet/blob/master/NWNDefinitions/Entities/Creature.cs
The .Net plugin is good for creating in-memory systems external to NWN.
Eg: Storing complex relationships and structures.
Multi-threaded computations
Accessing WebServices / Micro-Services
EntityFramework interaction |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|