Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

Python Scripts | Merry Christmas 2016!

Interesting...
Loyal Member
Joined
Oct 25, 2008
Messages
1,372
Reaction score
604
Is your server trash because it is exactly like everybody else's? Are you desperately looking for anything unique to differentiate your server from the competition? Well, look no further.

Introducing Python scripts! Think about the level of prestige, status and elitism that comes with being the only public live server that uses Python for its scripts. You can put that feature on your gtop listing and draw in hundreds of new players.

Alright so let's get started on implementing this bad boy. I did this implementation on the masterrace source Solaxia but since Odin's scripting engine hasn't really been touched since the Vietnam war, this should work on every source.

In ServerConstants, add this:
PHP:
//Scripting Configuration
public static final String SCRIPTING_ENGINE = "javascript";
public static final String SCRIPT_EXTENSION = ".js";

This helps modularize the language used for scripts in your source so you can painlessly change it back to Javascript should you choose to, or perhaps even use another language entirely.

The following changes are going to be pretty repetitive but I will still list out all. In EventScriptManager, replace
PHP:
Invocable iv = getInvocable("event/" + script + ".js", null);
with
PHP:
Invocable iv = getInvocable("event/" + script + ServerConstants.SCRIPT_EXTENSION, null);

In ItemScriptManager, replace
PHP:
File scriptFile = new File("scripts/item/" + scriptName + ".js");
File scriptFile = new File("scripts/item/" + scriptName + ".js");
sef = sem.getEngineByName("javascript").getFactory();
with
PHP:
File scriptFile = new File("scripts/item/" + scriptName + ServerConstants.SCRIPT_EXTENSION);
File scriptFile = new File("scripts/item/" + scriptName + ServerConstants.SCRIPT_EXTENSION);
sef = sem.getEngineByName(ServerConstants.SCRIPTING_ENGINE).getFactory();

In MapScriptManager, replace
PHP:
sef = sem.getEngineByName("javascript").getFactory();
File scriptFile = new File("scripts/map/" + (firstUser ? "onFirstUserEnter/" : "onUserEnter/") + scriptName + ".js");
File scriptFile = new File("scripts/map/" + type + scriptName + ".js");
with
PHP:
sef = sem.getEngineByName(ServerConstants.SCRIPTING_ENGINE).getFactory();
File scriptFile = new File("scripts/map/" + (firstUser ? "onFirstUserEnter/" : "onUserEnter/") + scriptName + ServerConstants.SCRIPT_EXTENSION);
File scriptFile = new File("scripts/map/" + type + scriptName + ServerConstants.SCRIPT_EXTENSION);

In NPCScriptManager, replace
PHP:
iv = getInvocable("npc/world" + c.getWorld() + "/" + fileName + ".js", c);
iv = getInvocable("npc/world" + c.getWorld() + "/" + npc + ".js", c);
resetContext("npc/world" + c.getWorld() + "/" + cm.getScriptName() + ".js", c);
resetContext("npc/world" + c.getWorld() + "/" + cm.getNpc() + ".js", c);
with
PHP:
iv = getInvocable("npc/world" + c.getWorld() + "/" + fileName + ServerConstants.SCRIPT_EXTENSION, c);
 iv = getInvocable("npc/world" + c.getWorld() + "/" + npc + ServerConstants.SCRIPT_EXTENSION, c);
resetContext("npc/world" + c.getWorld() + "/" + cm.getScriptName() + ServerConstants.SCRIPT_EXTENSION, c);
resetContext("npc/world" + c.getWorld() + "/" + cm.getNpc() + ServerConstants.SCRIPT_EXTENSION, c);

In PortalScriptManager, replace
PHP:
sef = sem.getEngineByName("javascript").getFactory();
File scriptFile = new File("scripts/portal/" + scriptName + ".js");
with
PHP:
sef = sem.getEngineByName(ServerConstants.SCRIPTING_ENGINE).getFactory();
File scriptFile = new File("scripts/portal/" + scriptName + ServerConstants.SCRIPT_EXTENSION);

In QuestScriptManager, replace
PHP:
Invocable iv = getInvocable("quest/" + questid + ".js", c);
Invocable iv = getInvocable("quest/" + questid + ".js", c);
resetContext("quest/" + qm.getQuest() + ".js", c);
with
PHP:
Invocable iv = getInvocable("quest/" + questid + ServerConstants.SCRIPT_EXTENSION, c);
Invocable iv = getInvocable("quest/" + questid + ServerConstants.SCRIPT_EXTENSION, c);
resetContext("quest/" + qm.getQuest() + ServerConstants.SCRIPT_EXTENSION, c);

In ReactorScriptManager, replace
PHP:
Invocable iv = getInvocable("reactor/" + reactor.getId() + ".js", c);
Invocable iv = getInvocable("reactor/" + reactor.getId() + ".js", c);
with
PHP:
Invocable iv = getInvocable("reactor/" + reactor.getId() + ServerConstants.SCRIPT_EXTENSION, c);
Invocable iv = getInvocable("reactor/" + reactor.getId() + ServerConstants.SCRIPT_EXTENSION, c);

Finally, in your AbstractScriptManager, replace
PHP:
engine = sem.getEngineByName("javascript");
if (ServerConstants.JAVA_8){
    engine.eval("load('nashorn:mozilla_compat.js');");
}
with
PHP:
engine = sem.getEngineByName(ServerConstants.SCRIPTING_ENGINE);
if (ServerConstants.JAVA_8 && ServerConstants.SCRIPTING_ENGINE.equals("javascript")){
    engine.eval("load('nashorn:mozilla_compat.js');");
}

And there you have it, the implementation is that easy. This could obviously be accomplished with a simple IDE search + replace, but I listed out all the steps anyways. Now that you can switch your source's scripting language, let's switch it to Python boyssssssssssssss.

PHP:
//Scripting Configuration
public static final String SCRIPTING_ENGINE = "python";
public static final String SCRIPT_EXTENSION = ".py";

We will need a dependency for this to actually work, and for this we will use Jython. Grab yourself a copy by . I used Jython 2.5.4rc1 so I recommend you also use that. I cannot vouch for Jython 2.7.0 or any other version since I did not personally test it on those. Once you have your standalone jar, put in your dist folder and add it as an external jar like you would when originally setting up any source. Once you have done this, your source is fully ready to read and evaluate python scripts!

Being Christmas and all, I have decided to release a carbon-copy conversion of a random Javascript NPC script into Python, just to show how relatively easy it actually is to switch over entirely.

Below is a basic NPC script in Javascript written by RageZone's greatest member ever, Moogra, and then followed by my carbon-copy version in Python.
PHP:
/*
	This file is part of the OdinMS Maple Story Server
    Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
		       Matthias Butz <matze@odinms.de>
		       Jan Christian Meyer <vimes@odinms.de>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation version 3 as published by
    the Free Software Foundation. You may not use, modify or distribute
    this program under any other version of the GNU Affero General Public
    License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/* NPC:     Thomas Swift
 * Maps:    100000000, 680000000
 * Author:  Moogra
 * Purpose: Amoria warper.
*/

status = -1;

function start() {
    if (cm.getPlayer().getMapId() == 100000000)
        cm.sendYesNo("I can take you to the Amoria Village. Are you ready to go?");
    else
        cm.sendYesNo("I can take you back to Henesys. Are you ready to go?");
}

function action(mode, type, selection) {
    status++;
    if (mode != 1) {
        if (mode == 0)
            cm.sendOk("Ok, feel free to hang around until you're ready to go!");
        cm.dispose();
        return;
    }
    if (status == 0)
        cm.sendNext("I hope you had a great time! See you around!");
    else if (status == 1) {
        if (cm.getPlayer().getMapId() == 100000000)
            cm.warp(680000000, 0);
        else
            cm.warp(100000000, 5);
        cm.dispose();
    }
}
and now my version in Python
PHP:
'''
	This file is part of the OdinMS Maple Story Server
    Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
		       Matthias Butz <matze@odinms.de>
		       Jan Christian Meyer <vimes@odinms.de>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation version 3 as published by
    the Free Software Foundation. You may not use, modify or distribute
    this program under any other version of the GNU Affero General Public
    License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''
''' NPC:     Thomas Swift
    Maps:    100000000, 680000000
    Author:  Moogra
    Purpose: Amoria warper.
'''

status = -1;

def start():
    if (cm.getPlayer().getMapId() == 100000000):
        cm.sendYesNo("I can take you to the Amoria Village. Are you ready to go?")
    else:
        cm.sendYesNo("I can take you back to Henesys. Are you ready to go?")

def action(mode, type, selection):
    global status
    status += 1
    if (mode != 1):
        if (mode == 0):
            cm.sendOk("Ok, feel free to hang around until you're ready to go!")
        cm.dispose()
        return
    if (status == 0):
        cm.sendNext("I hope you had a great time! See you around!")
    elif (status == 1):
        if (cm.getPlayer().getMapId() == 100000000):
            cm.warp(680000000, 0)
        else:
            cm.warp(100000000, 5)
        cm.dispose()

As you can see, it practically takes no effort at all to convert. There are virtually no real differences between those two above scripts. As a general overview, you simply add a colon where an opening brace would be, delete all closing braces and semicolons, use Python's comment syntax instead of Javascript's, and you're already like literally 90% of the way done converting. The remaining 10% will simply be resolving any little quirks unique to Python like
PHP:
global status
Without going into too much detail since this obviously isn't a Python tutorial thread, without that line, Python would read the variable "status"
PHP:
status += 1
as a new local variable instead of the externally defined one. The global keyword tells Python that you are referring to the "status" variable defined outside of action's scope, and thus it now works exactly as you would expect it to, like the Javascript version.

Frequently Asked Questions
1) Okay that's cool but all my scripts are in Javascript...?
lol tru
2) So basically you want me to rewrite all my scripts to Python?
lol yea tbh
3) So is there any practical use for this release?
In all seriousness, it's cool to do things differently and while it probably isn't worth the effort to convert all your scripts over, I'm like 85% confident I could simply write a tool to translate all OdinMS Javascript scripts to Python in such a way that you could realistically be fully converted in just a few hours of work from start to finish. A couple hours of work for a pretty unique feature like this with little to no side effect (at least none I can think of right now) could be worth it depending on how you view things. So to ultimately answer this question, it is legitimately practical if you actually want to put in a bit of effort to make the switch entirely. Otherwise, then no, this release isn't all that practical for you. I just needed something memey af to release for Christmas and this was perfect.
4) So are you also releasing that conversion tool?
If by some miracle there is legitimate interest in Python scripts, then yeah, I actually would write and release a conversion tool. But obviously not if nobody would use it.

Shameless Plug
If you're not part of the MapleStory development discord by now, you should certainly join it by . You can penis around with the greatest memers around including the likes of Zygon, Eric, PacketBakery and many others.
SharpAceX - Python Scripts | Merry Christmas 2016! - RaGEZONE Forums

Merry Christmas everybody!

:love:
 
Newbie Spellweaver
Joined
Nov 7, 2015
Messages
98
Reaction score
3
I honestly do not think that Python will attract new players, but thank you for the release.
 
Joined
Oct 14, 2008
Messages
960
Reaction score
197
This idea is actually very interesting (though I don't need your code, lol).
I will consider using Python as the scripting engine for my C++ source.
I have never thought of this before, thank you.
 
Interesting...
Loyal Member
Joined
Oct 25, 2008
Messages
1,372
Reaction score
604
I honestly do not think that Python will attract new players, but thank you for the release.

Clearly you have never run a popular server in your life. Python DOES attract new players.

de_memes...?

@op ty for rls, i actually use python as well =)
masterrace!

SharpAceX - Python Scripts | Merry Christmas 2016! - RaGEZONE Forums

Nice, at least they aren't hardcoded. :wink:

This idea is actually very interesting (though I don't need your code, lol).
I will consider using Python as the scripting engine for my C++ source.
I have never thought of this before, thank you.

Let's launch the Python revolution and take down the Javascript empire.
 
Junior Spellweaver
Joined
Mar 21, 2015
Messages
126
Reaction score
13
Down with js!

Edit: Groovy would also be a cool substitute
 
Newbie Spellweaver
Joined
Nov 7, 2015
Messages
98
Reaction score
3
Clearly you have never run a popular server in your life. Python DOES attract new players.

No need to get aggressive; I was just stating my opinion from the perspective of a player.
 
Back
Top