PartyCraft Service

Feb 4, 2012 at 6:45 PM
Edited Feb 4, 2012 at 6:51 PM

Hi

I've created this topic to communicate on the PartyCraft win32 Service I'm working on and that needs some adjustments to the PartyCraft core and some discussion i think.

Currently I've updated to the latest PartyCraft code (88034) and altough the code in progam.cs has been minimized I still miss a couple of things in order to to keep my service code running.

For now most code seems to be roughly compatible enough (could delete most of the code in mt PartyCraftService.cs but there are some issues.

  • The first issue is the Login. As Log() is not an event it's impossible to implement other logging (for a service I would like to log into the Win32 eventlog). So question, could you please turn Log into an event so i can hook up with it and PartyCraft.cs etc calls into mty service code where the eventlog stuff is implemented.
  • Relates are two other events, two of which i've extended with Log (OnPlayerLeave) or would like to extend (OnSettinsgChanged). These two could have a call to Log implemented in the PartyCraft code (think it would be a good feature anyway).

In my case OnPlayerLEave looks like:

static void Server_OnPlayerLeave(object sender, PlayerEventArgs e) {
  String msg = string.Format(Settings["server.messages.playerleave" ], e.Player.PlayerEntity.Name);
  Server.SendChat(msg);
  Log(msg);
}

  • For OnSettinsgChanged I would like to Log  who has changed what and when.
     
    More problemetic is Server_OnPlayerJoin, unless it's implemented into the PartyCraft Core. As a service does not have the built-in Console users (bad idea anyway imho btw) I need at least one predefined Operator in the XmlSettinsg (so he/she can turn other people into operators). If not implemented in the core I need to be able to replace the Core's OnPlayer Server_OnPlayerJoin by my own. I Can register another (multicast)event handler but I'm not sure of the order in which these are called.

My Server_OnPlayerJoin looks like:

static void Server_OnPlayerJoin(object sender, PlayerEventArgs e) {
  //! veg: Set operator properties on player:
  //! <operators>op1,op2</operators>.
  e.Player.Tags["op"] = Settings["operators.operator"].Split(new char[] { ',' }).Contains(e.Player.PlayerEntity.Name);
  if ((Boolean)e.Player.Tags["op"] == true) {
    Log("Settings turned {0} into an Operator" , e.Player.PlayerEntity.Name);
  }
  String msg = string.Format(Settings["server.messages.playerjoin" ], e.Player.PlayerEntity.Name);
  Server.SendChat(msg);
  Log(msg);
}
 
Again I think this code would be a good addition to the PartyCraft Core.

  • Finally my Log() method  has a conveniance overload like:

public static void Log(string format, params object [] args) {
  Log(String .Format(format, args));
}

And Log itself cleans up the '§' markers in of MineCraft before writing to the (event)log.

I almost forgot to mention that i added the PartyCraft code to my Service project by linking it (which keeps me from copying code into two places). It's a bit hidden feature in VS2k10 but it work nicely!

Keep up the coding!

wvd_vegt

Feb 4, 2012 at 6:58 PM

Hi

I noticed another change (now i deleted most PartyCraft.Run code from my service).

  • Besides RestartServer, I added a StopServer Boolean and added it to the end of the while loop in static void AsyncService() like

    while (true) {

    ....

    if (StopServer) {
      break
    }
  } //while
  Log(
"STOPPING SERVER");
  Server.Stop();
}

I need this in order to stop the Win32 PartyCraft Service nicely.

wvd_vegt

Coordinator
Feb 5, 2012 at 3:53 AM

If you give me a list of methods you would like made virtual in PartyCraft, I will do so.  This should be able to solve all of the problems by allowing you to modify whatever functionality you please.  I have added StopServer to the async service.

Feb 6, 2012 at 9:24 AM

Hi

I'm not sure about the three event (if it's a good idea to make them virtual). You might consider to add the small changes I proposed to the core.

At least Log() need to be virtual (as there is no event). But personally I dislike subclassing PartyCraft (I would prefer turning Log into an event would be much better and more in line with the rest of the events). You could have Log call an OnLog event if hooked. That way you could have your debug log as a 2nd hook and use the multicast event feature of C#.

I would like PartyCraft to do the actual work needed for the game and my code to do only the additional like event logging and op'ing players. That way if you change the code the service does not need adjustment.

Another solution might be to introduce as single OnPartyCraftEvent with a PlayerEventArgs and EventType (an enum with values like PlayerJoin, Playerleave, SettinsgChanged etc) that is called (if hooked) from your event handlers.

That way your code stays the way it is (assuming you do not hook the OnPartyCraftEvent but in my service I can add the stuff i needed after your code has done the actual work.

Btw if I want to create a front-end showing where players are, how do i get the XYZ coordinates of a player?

wvd_vegt

 

Coordinator
Feb 6, 2012 at 6:55 PM

I'll begin looking into your requested changes.  As for getting the location of a player, you want to get a client from MultiplayerServer.GetLoggedInClients(), and you'll find it in RemoteClient.PlayerEntity.Location.

Feb 14, 2012 at 8:28 AM

Hi

The code around the logstream still causes me problems insize the service (due to access limitations). I now managed to run the service under the network account (as it should be) but have some problems using the code from PartyCraft.cs

1) The logstream is created no matter the server.debugenabled setting (it should only be create and use if server.debugenabled is true imho).

I added thsi code to Run() to 'fix' it: 

 

            try
            {
                logStream = new StreamWriter(Settings["server.logfile"], true);
            }
            catch
            {
                Log("Could not create server.logfile");
            }

2) In Log the stacktrace is only genereated when server.debugenabled but the logstream is always written. It should only be  

Code like:

if (logStream != null) {
  logStream.WriteLine(prepend + Message);
  logStream.Flush();
}

would solve this problem in Log().

At the end of AsyncService() I added code to close the logStream (if assigned)

 

 

if (logStream != null){
logStream.Close();
}

Btw I'm working on a simple trayicon frontend at the moment.

wvd_vegt

Coordinator
Feb 19, 2012 at 10:26 PM

These changes have been implemented via PartyCraft.LogToFile

Mar 2, 2012 at 5:58 PM
Edited Mar 2, 2012 at 6:00 PM

Hi,

I finally had some time to do some catching up. I manged to get the logging working in both partycraft and my service by adding en event

I added the following code to PartyCraft.cs:

        //veg 01-03-2012
        public static event EventHandler<LogEventArgs> OnLog;

 and changed the Log methods like:

 //veg 01-03-2012
        public static void Log(string format, params object[] args)
        {
            Log(String.Format(format, args));
        }

        public static void Log(string Message)
        {
            try
            {
                if (Settings == null || Message == null)
                    return;
                string prepend = "[" + DateTime.Now.ToString("HH:mm:ss") + "] ";
                if (Settings["server.debugenabled"] == "true")
                {
                    StackTrace trace = new StackTrace();
                    prepend += trace.GetFrame(1).GetMethod().DeclaringType.Name + "." + trace.GetFrame(1).GetMethod().Name + ": ";
                }

                //veg 01-03-2012
                if (OnLog != null)
                    OnLog(null, new LogEventArgs(prepend, Message));

                if (LogToFile)
                {
                    logStream.WriteLine(prepend + Message);
                    logStream.Flush();
                }
            }
            catch
            {
            }
        }

Note that i removed the Debug.WriteLine in this method and moved it into Program.cs like:

        /// <summary>
        /// Run PartyCraft
        /// </summary>
        static void Main(string[] args)
        {
            PartyCraft.SettingsDirectory = Directory.GetCurrentDirectory();

            //veg 01-03-2012
            PartyCraft.OnLog += new EventHandler<LogEventArgs>(PartyCraft_OnLog);

            foreach (string arg in args)
            {
                if (arg.StartsWith("-sd:"))
                    PartyCraft.SettingsDirectory = arg.Substring(4); // TODO: More command line options
            }

            PartyCraft.Run();
        }

        //veg 01-03-2012
        static void PartyCraft_OnLog(object sender, LogEventArgs e)
        {
            Console.WriteLine(e.prepend + e.Message);
        }
In my server code I can now simply so my own eventlog logging. 
Note that another change to PartyCraft was made (to prevent 
my service from running into the Run methods main loop:
//veg: 01-03-2012 - Added loop
        public static void Run(Boolean loop = true)
        {
...
 Log("Server started successfully!");

            //veg: 01-03-2012 - Be able to skip this when running a service.
            if (!loop)
            {
                return;
            }
...
///Console code
IMHO doe sthe consoleinput code not belong in PartyCraft (if it where 
an assembly) but in program.cs. The above fix i made is good 
enough for me for the moment.

Finally the LogEventArgs class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PartyCraftServer
{
    public class LogEventArgs : EventArgs
    {

        public LogEventArgs(string prepend, string Message)
        {
            this.prepend = prepend;
            this.Message = Message;
        }

        public string prepend;
        public string Message;
    }
}
The result is now that the 'normal'logging is no longer part of
PartyCraft but of the program.cs. This allows for turning 
PartyCraft into an assembly and use Program.cs as a small 
demo/commandline version.
I can also subcribe to the other vents and do some additional 
logging there.
I also managed to get my server running directly from Visual Studio
(without installing a service) so i should (with some further 
tweaking) be able to use this as both a service and commandline version.
Because of the changed in the logging i no longer need a copy of 
PartyCraft.cs customized for my service but can use PartyCraft 
simply as an Assembly.
I only ran into problems (again) with your internal logging. 
IMHO the main porogram should be able to specify this logging 
for both PartyCraft and LubMineCraft in order to prevent acess 
violations. LibMineCraft does not expose a LogFileName property nor 
does it read the Settings.xml.
I also noticed that my java client can no longer login 
(after displaying Loading World it stops with a connection 
timeout). 
It will also not read any of my worlds (the mcr fiels had an 
unknown compressin methods and the latest 1.2.3 server 
I downloaded has *.mca files instead of *.mcr) But i gess 
thats more a LibMinecraft issue.
wvd_vegt

 

Coordinator
Mar 2, 2012 at 6:33 PM

Hi wvd_vegt,

I'll try to cover as much of this as I can.

I will implement an event-handler based system for logging.

I will change Run() to leave console input to the console program.

PartyCraft already works as an assembly; you can add references to an exe the same way you can to a dll.

I'd like to hear more about your ability to run your version as both a service and a console program.

LibMinecraft logging is intended only for use within LibMinecraft, and is far too detailed to be useful for production programs.  LibMinecraft itself only logs packet dumps, which is of no use to the end-user.

LibMinecraft is currently being updated to version 1.2.3, and you will be able to connect with this client when the update is complete.

Saving and loading worlds in the current version of LibMinecraft is done with a custom file format.  Anvil world loading will be implemented in the same update, Alpha 0.3.

I am also currently working on a means of overriding XmlSettings so one could, for instance, use a SQL database instead.

Mar 3, 2012 at 3:48 PM

Hi,

Fine about including the code/concepts. It saves me maintenance and copying code every update (and luckily both partycraft and libminecraft are developed actively!).

The reason I mention the logging is that if the service/console combo is running ok, you will still need the logging I guess (so it has to work one way or the other). Thats why i also want to be able to get it running in such way it does not interfere with normal operations (that is not the same as actually using it).

Not clear from your answer is if libminecraft is able to read a world from minecraft_server.

I've send a private message about the servcie code.

wvd_vegt

 

Coordinator
Mar 3, 2012 at 10:06 PM

The current version of LibMinecraft is unable to read from or write to official Minecraft worlds.  The next version will be able to.