cSharp Porting Pains for CommandToClient/CommandToServer
by Vince Gee · 02/19/2012 (10:03 pm) · 2 comments
In my process of making T3d compatible with cSharp, I ran into an issue. You cannot easily call commandToClient or commandToServer from the C++.
Up till now, my process has been to take the guts out of each defined console command and wrap it with a extern function. So, yeah, I have like a million externs, for every console command, object command etc, I have a corresponding extern. (Well almost... there is a lot of em)
So, I'm sitting here looking at my code, and I see...
Basically, the only way I had to fire the event was through an execute on the console. To be honest, this was really ticking me off. I've overcome so many obstacles, to have this ugly, underperforming console leveraging code in my beautiful cSharp was sickening.
So, I search the forums, and there, all I found was where people had exposed function that did the same thing, i.e. wrapped the console command with parameters as well. It appeared that no one had attempted to tackle this problem correctly. (Possibly, no one really had to...)
After looking at Net.cpp, I realized that hmmm, I could take a part of their code and um, copy it, and reuse it. So, in my .cpp where I have all my externs (yeah, its a REAL big .cpp file) I added.
And at the end of my code, I added the externs,
Finally, now when I want to call a commandToClient, I simple put:
I did get lazy though, typing in con.addtaggedstring over and over again, I made the routine check if the function was tagged and if it wasn't it would automatically tag it for you, so really all you need is
Weeee, I now have the ability to bypass the console for commandToClient and commandToServer!!!!!
P.S.: The reason I just didn't add the externs to Net.cpp was because I am trying to keep all of my code changes in my file. So to add my resource, you just include my cpp and wala! It's done. I didn't want people hunting and pecking through a dozen files adding externs here and there.
Up till now, my process has been to take the guts out of each defined console command and wrap it with a extern function. So, yeah, I have like a million externs, for every console command, object command etc, I have a corresponding extern. (Well almost... there is a lot of em)
So, I'm sitting here looking at my code, and I see...
con.Eval("commandToClient(" + player + ".client, 'setNumericalHealthHUD', " + curhealth.ToString("0") + ");");Basically, the only way I had to fire the event was through an execute on the console. To be honest, this was really ticking me off. I've overcome so many obstacles, to have this ugly, underperforming console leveraging code in my beautiful cSharp was sickening.
So, I search the forums, and there, all I found was where people had exposed function that did the same thing, i.e. wrapped the console command with parameters as well. It appeared that no one had attempted to tackle this problem correctly. (Possibly, no one really had to...)
After looking at Net.cpp, I realized that hmmm, I could take a part of their code and um, copy it, and reuse it. So, in my .cpp where I have all my externs (yeah, its a REAL big .cpp file) I added.
class RCE : public NetEvent
{
public:
typedef NetEvent Parent;
enum {
MaxRemoteCommandArgs = 20,
CommandArgsBits = 5
};
private:
S32 mArgc;
char *mArgv[MaxRemoteCommandArgs + 1];
NetStringHandle mTagv[MaxRemoteCommandArgs + 1];
static char mBuf[1024];
public:
RCE(S32 argc=0, const char **argv=NULL, NetConnection *conn = NULL)
{
mArgc = argc;
for(S32 i = 0; i < argc; i++)
{
if(argv[i][0] == StringTagPrefixByte)
{
char buffer[256];
mTagv[i+1] = NetStringHandle(dAtoi(argv[i]+1));
if(conn)
{
dSprintf(buffer + 1, sizeof(buffer) - 1, "%d", conn->getNetSendId(mTagv[i+1]));
buffer[0] = StringTagPrefixByte;
mArgv[i+1] = dStrdup(buffer);
}
}
else
mArgv[i+1] = dStrdup(argv[i]);
}
}
~RCE()
{
for(S32 i = 0; i < mArgc; i++)
dFree(mArgv[i+1]);
}
.....(look in net.cpp for the missing code)
DECLARE_CONOBJECT(RCE);
};
char RCE::mBuf[1024];
IMPLEMENT_CO_NETEVENT_V1(RCE);
static void dnc_sendRemoteCommand(NetConnection *conn, S32 argc, const char **argv)
{
if(U8(argv[0][0]) != StringTagPrefixByte)
{
Con::errorf(ConsoleLogEntry::Script, "Remote Command Error - command must be a tag.");
return;
}
S32 i;
for(i = argc - 1; i >= 0; i--)
{
if(argv[i][0] != 0)
break;
argc = i;
}
for(i = 0; i < argc; i++)
{
//Con::errorf("Param %i = '%s'",i,argv[i]);
conn->validateSendString(argv[i]);
}
RCE *cevt = new RCE(argc, argv, conn);
conn->postNetEvent(cevt);
}And at the end of my code, I added the externs,
extern "C" __declspec(dllexport) void commandToServer(S32 argc,char ** _argv)
{
std::vector<const char*> arguments;
for (int i =0;i<argc;i++)
{
arguments.push_back(_argv[i]);
}
const char** argv = &arguments[0];
NetConnection *conn = NetConnection::getConnectionToServer();
if(!conn)
return;
dnc_sendRemoteCommand(conn, argc - 1, argv + 1);
}
extern "C" __declspec(dllexport) void commandToClient(S32 argc,char ** _argv)
{
std::vector<const char*> arguments;
for (int i =0;i<argc;i++)
{
arguments.push_back(_argv[i]);
}
const char** argv = &arguments[0];
NetConnection *conn;
if(!Sim::findObject(argv[1], conn))
{
Con::errorf("Unable to find connection %s" ,argv[1] );
return;
}
dnc_sendRemoteCommand(conn, argc - 2, argv + 2);
}Finally, now when I want to call a commandToClient, I simple put:
con.commandToClient(con.GetVarString(player + ".client"), con.addTaggedString("setNumericalHealthHUD"),new string[ ]{curhealth.AsString()});I did get lazy though, typing in con.addtaggedstring over and over again, I made the routine check if the function was tagged and if it wasn't it would automatically tag it for you, so really all you need is
con.commandToClient(con.GetVarString(player + ".client"),"setNumericalHealthHUD",new string[ ]{curhealth.AsString()});Weeee, I now have the ability to bypass the console for commandToClient and commandToServer!!!!!
P.S.: The reason I just didn't add the externs to Net.cpp was because I am trying to keep all of my code changes in my file. So to add my resource, you just include my cpp and wala! It's done. I didn't want people hunting and pecking through a dozen files adding externs here and there.
About the author
www.winterleafentertainment.com
#2
02/20/2012 (6:39 am)
It's worth learning C#. You can use it for external tools and such because it's easier to use than C++ (shorter dev time) and it's as safe as Java (managed, garbage collection, safer "pointers"). And it's similar enough to C++ and even TorqueScript that you should be able to leverage at least programming practices between the three.
Torque Owner David Robert Pemberton
www.deadlyassets.com