- 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:
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
with
In ItemScriptManager, replace
with
In MapScriptManager, replace
with
In NPCScriptManager, replace
with
In PortalScriptManager, replace
with
In QuestScriptManager, replace
with
In ReactorScriptManager, replace
with
Finally, in your AbstractScriptManager, replace
with
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.
We will need a dependency for this to actually work, and for this we will use Jython. Grab yourself a copy by
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.
and now my version in Python
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
Without going into too much detail since this obviously isn't a Python tutorial thread, without that line, Python would read the variable "status"
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
Shameless Plug
Merry Christmas everybody!
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);
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();
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");
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);
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");
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);
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);
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');");
}
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
You must be registered to see links
. 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();
}
}
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
PHP:
status += 1
Frequently Asked Questions
1) Okay that's cool but all my scripts are in Javascript...?
2) So basically you want me to rewrite all my scripts to Python?
3) So is there any practical use for this release?
4) So are you also releasing that conversion tool?
lol tru
lol yea tbh
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.
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 must be registered to see links
. You can penis around with the greatest memers around including the likes of Zygon, Eric, PacketBakery and many others.Merry Christmas everybody!