Experimental AutoJCE & Reactor's incorrect parameter

[EXPERIMENTAL] Added the AutoJCE feature to the server source. This is expected to solve the encryption size issue without resorting to overwrite the JCE files on the system.
LMPQ now sends players to random starting rooms, GMS-like.
Added a server flag for the free market item sold announcement.
Tweaked the reactor-hitting system, expecting to solve the incorrect parameter issue on the "multiple players hitting" scenario.
This commit is contained in:
ronancpl
2018-04-19 22:03:12 -03:00
parent 134c34fa58
commit a1fcf21ac9
19 changed files with 241 additions and 72 deletions

View File

@@ -869,3 +869,7 @@ Adicionado drops faltando da questline Puppeteer de Aran.
Movimentação de GM rank para alguns comandos.
Corrigido autoassigner inesperadamente desconectando jogadores, quando o autoassigner do cliente está sendo utilizado.
Adicionado scripts para a questline de Full Swing de Aran.
19 Março 2018,
Tentativa de correção em reactors desconectando jogadores que tentam ativá-los com ataque básico ao mesmo tempo.
Adicionado feature de AutoJCE (créditos ao Kradi-a).

View File

@@ -3,6 +3,12 @@ Credits:
Ronan - Freelance Developer
Vcoc - Freelance Developer
---------------------------
Known issues:
- Everytime two people click on an npc at the same time, one of them dcs and the other needs to @dispose to talk to the npc.
- If multiple people hit boxes/reactors at the same time, they both dc with invalid pointer error.
---------------------------
---------------------------
ToDo / Missing features list:
---------------------------

View File

@@ -102,7 +102,9 @@ function afterSetup(eim) {}
function respawnStages(eim) {}
function playerEntry(eim, player) {
var map = eim.getMapInstance(entryMap);
var rand = Math.floor(Math.random() * 15);
var map = eim.getMapInstance(entryMap + rand);
player.changeMap(map, map.getPortal(0));
}

81
scripts/quest/21735.js Normal file
View File

@@ -0,0 +1,81 @@
/*
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
Copyleft (L) 2017 RonanLana
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/>.
*/
var status = -1;
function start(mode, type, selection) {
if (mode == -1) {
qm.dispose();
} else {
if(mode == 0 && type > 0) {
qm.dispose();
return;
}
if (mode == 1)
status++;
else
status--;
if (status == 0) {
qm.sendNext("Aran, ever since the Puppeteer's ambush on me, I've been thinking it is dangerous to have the #b#t4032323##k around here by myself. So, I need you to deliver the gem to #r#p1201000##k, in Rien, she will know what to do with it.");
} else if (status == 1) {
if(!qm.canHold(4032323, 1)) {
qm.sendNext("Please free a slot on your ETC inventory before receiving the item.");
qm.dispose();
return;
}
if(!qm.haveItem(4032323, 1)) qm.gainItem(4032323, 1);
qm.forceStartQuest();
qm.dispose();
}
}
}
function end(mode, type, selection) {
if (mode == -1) {
qm.dispose();
} else {
if(mode == 0 && type > 0) {
qm.dispose();
return;
}
if (mode == 1)
status++;
else
status--;
if (status == 0) {
if(qm.haveItem(4032323, 1)) {
qm.sendNext("#r#p1002104##k sent the #b#t4032323##k here for safety? Thank goodness, indeed here the gem will be safer than anywhere on Victoria Island. Thank you, #b#h0##k.");
} else {
qm.dispose();
}
} else if (status == 1) {
qm.gainItem(4032323, -1);
qm.gainExp(6037 * qm.getPlayer().getExpRate());
qm.forceCompleteQuest();
qm.dispose();
}
}
}

View File

@@ -9,7 +9,7 @@ function start(mode, type, selection) {
} else if (status == 2) {
qm.sendNext("I have a feeling there is a secret behind that wooden box. Could you stealthily look into the wooden box next to #p20000#?");
} else if (status == 3) {
qm.sendNext("You know where #p20000# is, right? Hes to the right. Just keep going until you see where Vikin is, then head down past the hanging shark and octopus, and you''ll see John. The box should be right next to him.");
qm.sendNext("You know where #p20000# is, right? He's to the right. Just keep going until you see where Vikin is, then head down past the hanging shark and octopus, and you''ll see John. The box should be right next to him.");
} else {
qm.forceStartQuest();
qm.dispose();

View File

@@ -58,7 +58,7 @@ function end(mode, type, selection) {
} else {
qm.sendNext("You seem pretty normal, don't you? I can't detect any possible effect from my experiment on you. Go take the pill I asked you to take and show me the effects, will you?");
}
} else {
qm.dispose();
}
}

View File

@@ -19767,7 +19767,7 @@ USE `heavenms`;
(2230110, 4032146, 1, 1, 20722, 40000),
(2230111, 4032147, 1, 1, 20723, 40000),
(9300347, 4001272, 1, 1, 0, 400000),
(9300347, 4032324, 1, 1, 21736, 40000),
(9300347, 4032324, 1, 1, 21737, 40000),
(9300344, 4032322, 1, 1, 21731, 999999),
(3400008, 1302008, 1, 1, 0, 4200),
(3400008, 1412004, 1, 1, 0, 4200),

View File

@@ -73,9 +73,9 @@ public class ServerConstants {
public static final boolean USE_QUEST_RATE = false; //Exp/Meso gained by quests uses fixed server exp/meso rate times quest rate as multiplier, instead of player rates.
public static final boolean USE_MULTIPLE_SAME_EQUIP_DROP = true;//Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data.
//Announcement Configuration
public static final boolean USE_ANNOUNCE_CHANGEJOB = true; //Automatic message sent to acquantainces when changing jobs.
public static final boolean USE_ANNOUNCE_SHOPITEMSOLD = false; //Automatic message sent to owner when an item from the Player Shop or Hired Merchant is sold.
public static final boolean USE_ANNOUNCE_CHANGEJOB = false; //Automatic message sent to acquantainces when changing jobs.
//Server Rates And Experience
public static final int EXP_RATE = 10;

View File

@@ -112,13 +112,12 @@ public class MapleServerHandler extends IoHandlerAdapter {
FilePrinter.print(FilePrinter.SESSION, "IoSession with " + session.getRemoteAddress() + " opened on " + sdf.format(Calendar.getInstance().getTime()), false);
}
byte key[] = {0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, (byte) 0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00};
byte ivRecv[] = {70, 114, 122, 82};
byte ivSend[] = {82, 48, 120, 115};
ivRecv[3] = (byte) (Math.random() * 255);
ivSend[3] = (byte) (Math.random() * 255);
MapleAESOFB sendCypher = new MapleAESOFB(key, ivSend, (short) (0xFFFF - ServerConstants.VERSION));
MapleAESOFB recvCypher = new MapleAESOFB(key, ivRecv, (short) ServerConstants.VERSION);
MapleAESOFB sendCypher = new MapleAESOFB(ivSend, (short) (0xFFFF - ServerConstants.VERSION));
MapleAESOFB recvCypher = new MapleAESOFB(ivRecv, (short) ServerConstants.VERSION);
MapleClient client = new MapleClient(sendCypher, recvCypher, session);
client.setWorld(world);
client.setChannel(channel);

View File

@@ -71,10 +71,12 @@ import client.SkillFactory;
import client.newyear.NewYearCardRecord;
import constants.ItemConstants;
import constants.ServerConstants;
import java.security.Security;
import java.util.Calendar;
import net.server.audit.ThreadTracker;
import server.quest.MapleQuest;
import tools.locks.MonitoredLockType;
import tools.AutoJCE;
public class Server implements Runnable {
private static final Set<Integer> activeFly = new HashSet<>();
@@ -382,6 +384,8 @@ public class Server implements Runnable {
public static void main(String args[]) {
System.setProperty("wzpath", "wz");
Security.setProperty("crypto.policy", "unlimited");
AutoJCE.removeCryptographyRestrictions();
Server.getInstance().run();
}

View File

@@ -41,7 +41,7 @@ public final class ReactorHitHandler extends AbstractMaplePacketHandler {
slea.skip(4);
int skillid = slea.readInt();
MapleReactor reactor = c.getPlayer().getMap().getReactorByOid(oid);
if (reactor != null && reactor.isAlive()) {
if (reactor != null) {
reactor.hitReactor(true, charPos, stance, skillid, c);
}
}

View File

@@ -43,6 +43,7 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
Server.getInstance().unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
String[] socket = Server.getInstance().getIP(c.getWorld(), c.getChannel()).split(":");
try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));

View File

@@ -45,7 +45,7 @@ public class NPCScriptManager extends AbstractScriptManager {
private Map<MapleClient, Invocable> scripts = new HashMap<>();
private static NPCScriptManager instance = new NPCScriptManager();
public synchronized static NPCScriptManager getInstance() {
public static NPCScriptManager getInstance() {
return instance;
}
@@ -140,8 +140,9 @@ public class NPCScriptManager extends AbstractScriptManager {
}
public void dispose(MapleClient c) {
if (cms.get(c) != null) {
dispose(cms.get(c));
NPCConversationManager cm = cms.get(c);
if (cm != null) {
dispose(cm);
}
}

View File

@@ -29,6 +29,7 @@ import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import com.mysql.jdbc.Statement;
import constants.ItemConstants;
import constants.ServerConstants;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -192,7 +193,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
if (c.getPlayer().getMeso() >= price) {
if (canBuy(c, newItem)) {
c.getPlayer().gainMeso(-price, false);
announceItemSold(newItem, price); // idea thanks to vcoc
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to vcoc
synchronized (sold) {
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price));

View File

@@ -25,6 +25,7 @@ import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import constants.ServerConstants;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
@@ -35,7 +36,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import net.SendOpcode;
import net.server.Server;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import tools.MaplePacketCreator;
@@ -216,7 +216,7 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
if (canBuy(c, newItem)) {
c.getPlayer().gainMeso(-price, false);
announceItemSold(newItem, price); // idea thanks to vcoc
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to vcoc
owner.gainMeso(price, true);
pItem.setBundles((short) (pItem.getBundles() - quantity));

View File

@@ -55,6 +55,7 @@ public class MapleReactor extends AbstractMapleMapObject {
private boolean attackHit;
private ScheduledFuture<?> timeoutTask = null;
private Lock reactorLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR, true);
private Lock hitLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR, true);
public MapleReactor(MapleReactorStats stats, int rid) {
this.evstate = (byte)0;
@@ -140,6 +141,10 @@ public class MapleReactor extends AbstractMapleMapObject {
return alive;
}
public boolean isActive() {
return alive && stats.getType(state) != -1;
}
public void setAlive(boolean alive) {
this.alive = alive;
}
@@ -227,12 +232,13 @@ public class MapleReactor extends AbstractMapleMapObject {
hitReactor(false, 0, (short) 0, 0, c);
}
public synchronized void hitReactor(boolean wHit, int charPos, short stance, int skillid, MapleClient c) {
public void hitReactor(boolean wHit, int charPos, short stance, int skillid, MapleClient c) {
try {
if(!this.isAlive()) {
if(!this.isActive()) {
return;
}
if(hitLock.tryLock()) {
this.lockReactor();
try {
cancelReactorTimeout();
@@ -291,6 +297,9 @@ public class MapleReactor extends AbstractMapleMapObject {
} finally {
this.unlockReactor();
}
hitLock.unlock();
}
} catch(Exception e) {
e.printStackTrace();
}

61
src/tools/AutoJCE.java Normal file
View File

@@ -0,0 +1,61 @@
package tools;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Map;
public class AutoJCE{ // AutoJCE into server source thanks to Kradi-a
/**
* Credits: ntoskrnl of StackOverflow
* http://stackoverflow.com/questions/1179672/
*/
public static byte removeCryptographyRestrictions(){
if(!isRestrictedCryptography()){
//System.out.println("Cryptography restrictions removal not needed");
return 0;
}
try{
/*
* Do the following, but with reflection to bypass access checks:
*
* JceSecurity.isRestricted = false;
* JceSecurity.defaultPolicy.perms.clear();
* JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
*/
final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");
final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");// was set to final in Java 8 Update 112. Requires you to remove the final modifier.
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
isRestrictedField.setAccessible(true);
isRestrictedField.set(null, false);
final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
defaultPolicyField.setAccessible(true);
final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);
final Field perms = cryptoPermissions.getDeclaredField("perms");
perms.setAccessible(true);
((Map<?, ?>) perms.get(defaultPolicy)).clear();
final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
instance.setAccessible(true);
defaultPolicy.add((Permission) instance.get(null));
//System.out.println("Successfully removed cryptography restrictions");
return 1;
}catch(final Exception e){
e.printStackTrace();
System.err.println("Failed to remove cryptography restrictions");
return -1;
}
}
private static boolean isRestrictedCryptography(){
// This simply matches the Oracle JRE, but not OpenJDK.
return "Java(TM) SE Runtime Environment".equals(System.getProperty("java.runtime.name"));
}
}

View File

@@ -33,6 +33,9 @@ public class MapleAESOFB {
private byte iv[];
private Cipher cipher;
private short mapleVersion;
private final static SecretKeySpec skey = new SecretKeySpec(
new byte[]{0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, (byte) 0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00}, "AES");
private static final byte[] funnyBytes = new byte[]{(byte) 0xEC, (byte) 0x3F, (byte) 0x77, (byte) 0xA4, (byte) 0x45, (byte) 0xD0, (byte) 0x71, (byte) 0xBF, (byte) 0xB7, (byte) 0x98, (byte) 0x20, (byte) 0xFC,
(byte) 0x4B, (byte) 0xE9, (byte) 0xB3, (byte) 0xE1, (byte) 0x5C, (byte) 0x22, (byte) 0xF7, (byte) 0x0C, (byte) 0x44, (byte) 0x1B, (byte) 0x81, (byte) 0xBD, (byte) 0x63, (byte) 0x8D, (byte) 0xD4, (byte) 0xC3,
(byte) 0xF2, (byte) 0x10, (byte) 0x19, (byte) 0xE0, (byte) 0xFB, (byte) 0xA1, (byte) 0x6E, (byte) 0x66, (byte) 0xEA, (byte) 0xAE, (byte) 0xD6, (byte) 0xCE, (byte) 0x06, (byte) 0x18, (byte) 0x4E, (byte) 0xEB,
@@ -51,19 +54,16 @@ public class MapleAESOFB {
(byte) 0xD3, (byte) 0xAB, (byte) 0x91, (byte) 0xB9, (byte) 0x84, (byte) 0x7F, (byte) 0x61, (byte) 0x1E, (byte) 0xCF, (byte) 0xC5, (byte) 0xD1, (byte) 0x56, (byte) 0x3D, (byte) 0xCA, (byte) 0xF4, (byte) 0x05,
(byte) 0xC6, (byte) 0xE5, (byte) 0x08, (byte) 0x49};
public MapleAESOFB(byte key[], byte iv[], short mapleVersion) {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
public MapleAESOFB(byte iv[], short mapleVersion) {
try {
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skey);
} catch (NoSuchAlgorithmException e) {
System.out.println("ERROR " + e);
} catch (NoSuchPaddingException e) {
System.out.println("ERROR " + e);
}
try {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
} catch (InvalidKeyException e) {
e.printStackTrace();
System.out.println("Error initalizing the encryption cipher. Make sure you're using the Unlimited Strength cryptography jar files.");
}
this.setIv(iv);
this.mapleVersion = (short) (((mapleVersion >> 8) & 0xFF) | ((mapleVersion << 8) & 0xFF00));