Public Repository for the Magicbane Shadowbane Emulator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1225 lines
45 KiB

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.net.client;
import engine.Enum.DispatchChannel;
import engine.Enum.GameObjectType;
import engine.InterestManagement.WorldGrid;
import engine.exception.MsgSendException;
import engine.gameManager.*;
import engine.job.JobContainer;
import engine.job.JobScheduler;
import engine.jobs.RefreshGroupJob;
import engine.jobs.StuckJob;
import engine.math.Vector3fImmutable;
import engine.net.Dispatch;
import engine.net.DispatchMessage;
import engine.net.NetMsgHandler;
import engine.net.client.handlers.AbstractClientMsgHandler;
import engine.net.client.msg.*;
import engine.net.client.msg.chat.AbstractChatMsg;
import engine.net.client.msg.commands.ClientAdminCommandMsg;
import engine.objects.*;
import engine.server.MBServerStatics;
import engine.server.world.WorldServer;
import engine.session.Session;
import engine.util.StringUtils;
import org.pmw.tinylog.Logger;
import java.sql.SQLException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import static engine.math.FastMath.sqr;
/**
* @author:
* @summary: This class is the mainline router for application protocol
* messages received by the client.
*/
public class ClientMessagePump implements NetMsgHandler {
// Instance variable declaration
private final WorldServer server;
public ClientMessagePump(WorldServer server) {
super();
this.server = server;
}
/*
* Incoming client protocol message are processed here
*/
private static void WhoRequest(WhoRequestMsg msg, ClientConnection origin) {
// Handle /who request
PlayerCharacter pc = origin.getPlayerCharacter();
if (pc == null)
return;
if (pc.getTimeStamp("WHO") > System.currentTimeMillis()) {
ErrorPopupMsg.sendErrorMsg(pc, "Who too fast! Please wait 3 seconds.");
return;
}
WhoResponseMsg.HandleResponse(msg.getSet(), msg.getFilterType(), msg.getFilter(), origin);
pc.getTimestamps().put("WHO", System.currentTimeMillis() + 3000);
}
// *** Refactor need to figure this out.
// Commented out for some reson or another.
private static void runWalkSitStand(UpdateStateMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
pc.update();
if (msg.getSpeed() == 2)
pc.setWalkMode(false);
else
pc.setWalkMode(true);
DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
}
private static void toggleLfgRecruiting(ToggleLfgRecruitingMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
int num = msg.toggleLfgRecruiting();
if (num == 1)
pc.toggleLFGroup();
else if (num == 2)
pc.toggleLFGuild();
else if (num == 3)
pc.toggleRecruiting();
UpdateStateMsg rwss = new UpdateStateMsg();
rwss.setPlayer(pc);
DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
}
private static void toggleSitStand(ToggleSitStandMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
pc.update();
pc.setSit(msg.toggleSitStand());
// cancel effects that break on sit
if (pc.isSit()) {
pc.setCombat(false);
pc.cancelOnSit();
}
UpdateStateMsg rwss = new UpdateStateMsg();
if (pc.isSit()) {
pc.setCombat(false);
rwss.setAware(1);
}
rwss.setPlayer(pc);
DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
}
private static void targetObject(TargetObjectMsg msg, ClientConnection origin) {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
// TODO improve this later. hacky way to make sure player ingame is
// active.
if (!pc.isActive())
pc.setActive(true);
pc.setLastTarget(GameObjectType.values()[msg.getTargetType()], msg.getTargetID());
}
private static void social(SocialMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, true);
}
//call this if the transfer fails server side to kick the item back to inventory from vault
public static void forceTransferFromInventoryToVault(ItemFromVaultMsg msg, ClientConnection origin, String reason) {
PlayerCharacter player = origin.getPlayerCharacter();
Dispatch dispatch;
if (player == null)
return;
ItemToVaultMsg back = new ItemToVaultMsg(msg);
dispatch = Dispatch.borrow(player, back);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
ChatManager.chatInfoError(player, "Can't transfer to inventory: " + reason);
}
//call this if the transfer fails server side to kick the item back to vault from inventory
public static void forceTransferFromVaultToInventory(ItemToVaultMsg msg, ClientConnection origin, String reason) {
PlayerCharacter player = origin.getPlayerCharacter();
Dispatch dispatch;
if (player == null)
return;
ItemFromVaultMsg back = new ItemFromVaultMsg(msg);
dispatch = Dispatch.borrow(player, back);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
ChatManager.chatInfoError(player, "Can't transfer to vault: " + reason);
}
private static void DeleteItem(DeleteItemMsg msg, ClientConnection origin) {
CharacterItemManager itemManager = origin.getPlayerCharacter().charItemManager;
int uuid = msg.getUUID();
PlayerCharacter sourcePlayer = origin.getPlayerCharacter();
if (sourcePlayer == null)
return;
if (!sourcePlayer.isAlive())
return;
Item i = Item.getFromCache(msg.getUUID());
if (i == null)
return;
if (!itemManager.doesCharOwnThisItem(i.getObjectUUID()))
return;
if (!itemManager.inventoryContains(i))
return;
if (i.isCanDestroy())
if (itemManager.delete(i) == true) {
Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
}
}
private static void ackBankWindowOpened(AckBankWindowOpenedMsg msg, ClientConnection origin) {
// According to the Wiki, the client should not send this message.
// Log the instance to investigate, and modify Wiki accordingly.
Logger.error(msg.toString());
}
private static void modifyStat(ModifyStatMsg msg, ClientConnection origin) {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
int type = msg.getType();
switch (type) {
case MBServerStatics.STAT_STR_ID:
pc.addStr(msg.getAmount());
break;
case MBServerStatics.STAT_DEX_ID:
pc.addDex(msg.getAmount());
break;
case MBServerStatics.STAT_CON_ID:
pc.addCon(msg.getAmount());
break;
case MBServerStatics.STAT_INT_ID:
pc.addInt(msg.getAmount());
break;
case MBServerStatics.STAT_SPI_ID:
pc.addSpi(msg.getAmount());
break;
}
}
// called when player clicks respawn button
private static void respawn(RespawnMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin);
if (sourcePlayer == null)
return;
if (msg.getObjectType() != sourcePlayer.getObjectType().ordinal() || msg.getObjectID() != sourcePlayer.getObjectUUID()) {
Logger.error("Player " + sourcePlayer.getObjectUUID() + " respawning character of id " + msg.getObjectType() + ' '
+ msg.getObjectID());
return;
}
if (sourcePlayer.isAlive()) {
Logger.error("Player " + sourcePlayer.getObjectUUID() + " respawning while alive");
return;
}
// ResetAfterDeath player
sourcePlayer.respawnLock.writeLock().lock();
try {
sourcePlayer.respawn(true, false, true);
} catch (Exception e) {
Logger.error(e);
} finally {
sourcePlayer.respawnLock.writeLock().unlock();
}
// Echo ResetAfterDeath message back
msg.setPlayerHealth(sourcePlayer.getHealth());
// TODO calculate any experience loss before this point
msg.setPlayerExp(sourcePlayer.getExp() + sourcePlayer.getOverFlowEXP());
Dispatch dispatch = Dispatch.borrow(sourcePlayer, msg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
MoveToPointMsg moveMsg = new MoveToPointMsg();
moveMsg.setPlayer(sourcePlayer);
moveMsg.setStartCoord(sourcePlayer.getLoc());
moveMsg.setEndCoord(sourcePlayer.getLoc());
moveMsg.setInBuilding(-1);
moveMsg.setInBuildingFloor(-1);
dispatch = Dispatch.borrow(sourcePlayer, moveMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
MovementManager.sendRWSSMsg(sourcePlayer);
// refresh the whole group with what just happened
JobScheduler.getInstance().scheduleJob(new RefreshGroupJob(sourcePlayer), MBServerStatics.LOAD_OBJECT_DELAY);
}
private static void lootWindowRequest(LootWindowRequestMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
if (!pc.isAlive())
return;
if (msg.getSourceType() != pc.getObjectType().ordinal() || msg.getSourceID() != pc.getObjectUUID()) {
Logger.error("Player " + pc.getObjectUUID() + " looting from character of id "
+ msg.getSourceType() + ' ' + msg.getSourceID());
return;
}
if (pc.getAltitude() > 0)
return;
if (!pc.isAlive()) {
return;
}
LootWindowResponseMsg lwrm = null;
GameObjectType targetType = GameObjectType.values()[msg.getTargetType()];
AbstractCharacter characterTarget = null;
Corpse corpseTarget = null;
switch (targetType) {
case PlayerCharacter:
characterTarget = PlayerCharacter.getFromCache(msg.getTargetID());
if (characterTarget == null)
return;
if (characterTarget.isAlive())
return;
if (pc.getLoc().distanceSquared2D(characterTarget.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) {
ErrorPopupMsg.sendErrorMsg(pc, "You are too far away to loot this corpse.");
Logger.info(pc.getFirstName() + " tried looting at " + pc.getLoc().distance2D(characterTarget.getLoc()) + " distance.");
return;
}
lwrm = new LootWindowResponseMsg(characterTarget.getObjectType().ordinal(), characterTarget.getObjectUUID(), characterTarget.getInventory(true));
break;
case NPC:
characterTarget = NPC.getFromCache(msg.getTargetID());
if (characterTarget == null)
return;
break;
case Mob:
characterTarget = Mob.getFromCache(msg.getTargetID());
if ((characterTarget == null) || characterTarget.isAlive()) {
return;
}
if (pc.getLoc().distanceSquared2D(characterTarget.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) {
ErrorPopupMsg.sendErrorMsg(pc, "You are too far away to loot this corpse.");
Logger.info(pc.getFirstName() + " tried looting at " + pc.getLoc().distance2D(characterTarget.getLoc()) + " distance.");
if (!((Mob) characterTarget).isLootSync()) {
((Mob) characterTarget).setLootSync(true);
WorldGrid.updateObject(characterTarget, pc);
}
return;
}
lwrm = new LootWindowResponseMsg(characterTarget.getObjectType().ordinal(), characterTarget.getObjectUUID(), characterTarget.getInventory());
break;
case Corpse:
corpseTarget = Corpse.getCorpse(msg.getTargetID());
if ((corpseTarget == null)) {
return;
}
if (pc.getLoc().distanceSquared(corpseTarget.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) {
ErrorPopupMsg.sendErrorMsg(pc, "You are too far away to loot this corpse.");
Logger.info(pc.getFirstName() + " tried looting at " + pc.getLoc().distance2D(characterTarget.getLoc()) + " distance.");
return;
}
lwrm = new LootWindowResponseMsg(corpseTarget.getObjectType().ordinal(), msg.getTargetID(), corpseTarget.getInventory());
break;
}
if (lwrm == null)
return;
DispatchMessage.dispatchMsgToInterestArea(pc, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
Dispatch dispatch = Dispatch.borrow(pc, lwrm);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
}
private static void loot(LootMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter player = SessionManager.getPlayerCharacter(origin);
if (player == null)
return;
if (!player.isAlive())
return;
Item item = msg.getItem();
if (item == null)
return;
if (item.lootLock.tryLock()) {
try {
Item itemRet = null;
// get current owner
int targetType = msg.getTargetType();
int targetID = msg.getTargetID();
if (targetType == GameObjectType.PlayerCharacter.ordinal() || targetType == GameObjectType.Mob.ordinal() || targetType == GameObjectType.Corpse.ordinal()) {
} else { //needed for getting contracts for some reason
targetType = msg.getSourceID2();
targetID = msg.getUnknown01();
}
//can't loot while flying
if (player.getAltitude() > 0)
return;
AbstractCharacter tar = null;
Corpse corpse = null;
if (targetType == GameObjectType.PlayerCharacter.ordinal() || targetType == GameObjectType.Mob.ordinal()) {
if (targetType == GameObjectType.PlayerCharacter.ordinal()) {
tar = PlayerCharacter.getFromCache(targetID);
if (tar == null)
return;
if (player.getObjectUUID() != tar.getObjectUUID() && ((PlayerCharacter) tar).isInSafeZone())
return;
} else if (targetType == GameObjectType.NPC.ordinal())
tar = NPC.getFromCache(targetID);
else if (targetType == GameObjectType.Mob.ordinal())
tar = Mob.getFromCache(targetID);
if (tar == null)
return;
if (tar.equals(player)) {
ErrorPopupMsg.sendErrorMsg(player, "Cannot loot this item.");
return;
}
if (player.getLoc().distanceSquared2D(tar.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) {
ErrorPopupMsg.sendErrorMsg(player, "You are too far away to loot this corpse.");
Logger.info(player.getFirstName() + " tried looting at " + player.getLoc().distance2D(tar.getLoc()) + " distance.");
return;
}
//can't loot from someone who is alive.
if (AbstractWorldObject.IsAbstractCharacter(tar)) {
if (tar.isAlive())
return;
// Logger.error("WorldServer.loot", "Looting from live player");
}
if (!GroupManager.goldSplit(player, item, origin, tar)) {
if (tar.charItemManager != null) {
itemRet = tar.charItemManager.lootItemFromMe(item, player, origin);
//Take equipment off mob
if (tar.getObjectType() == GameObjectType.Mob && itemRet != null) {
Mob mobTarget = (Mob) tar;
if (item != null && item.getObjectType() == GameObjectType.MobLoot) {
for (Item equip : mobTarget.charItemManager.equipped.values()) {
TransferItemFromEquipToInventoryMsg back = new TransferItemFromEquipToInventoryMsg(mobTarget, equip.equipSlot);
DispatchMessage.dispatchMsgToInterestArea(mobTarget, back, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
LootMsg lootMsg = new LootMsg(0, 0, tar.getObjectType().ordinal(), tar.getObjectUUID(), equip);
Dispatch dispatch = Dispatch.borrow(player, lootMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
break;
}
}
}
}
}
} else if (targetType == GameObjectType.Corpse.ordinal()) {
corpse = Corpse.getCorpse(targetID);
if (corpse == null)
return;
if (player.getLoc().distanceSquared2D(corpse.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) {
ErrorPopupMsg.sendErrorMsg(player, "You are too far away to loot this corpse.");
Logger.info(player.getFirstName() + " tried looting at " + player.getLoc().distance2D(corpse.getLoc()) + " distance.");
return;
}
//can't loot other players in safe zone.
if (corpse.getBelongsToType() == GameObjectType.PlayerCharacter.ordinal()) {
if (player.getObjectUUID() == corpse.getBelongsToID())
itemRet = corpse.lootItem(item, player);
else if (!GroupManager.goldSplit(player, item, origin, corpse)) {
itemRet = corpse.lootItem(item, player);
}
if (itemRet == null)
return;
if (item.template.item_type.equals(engine.Enum.ItemType.GOLD)) {
// this is done to prevent the temporary goldItem item
// (from the mob) from appearing in player's inventory.
// It also updates the goldItem quantity display
UpdateGoldMsg updateTargetGold = null;
if (corpse != null)
updateTargetGold = new UpdateGoldMsg(corpse);
updateTargetGold.configure();
DispatchMessage.dispatchMsgToInterestArea(corpse, updateTargetGold, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
UpdateGoldMsg ugm = new UpdateGoldMsg(player);
ugm.configure();
Dispatch dispatch = Dispatch.borrow(player, ugm);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
// respond back loot message. Try sending to everyone.
} else {
DispatchMessage.dispatchMsgToInterestArea(corpse, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, true);
//player.getCharItemManager().updateInventory();
}
//TODO send group loot message if player is grouped and visible
Group group = GroupManager.getGroup(player);
if (group != null && group.getSplitGold() && (item.template.item_type.equals(engine.Enum.ItemType.GOLD) == false)) {
String name = item.getName();
String text = player.getFirstName() + " has looted " + name + '.';
ChatManager.chatGroupInfoCanSee(player, text);
}
return;
}
} else
return;
if (itemRet == null) {
return;
}
if (item.template.item_type.equals(engine.Enum.ItemType.GOLD)) {
// this is done to prevent the temporary goldItem item
// (from the mob) from appearing in player's inventory.
// It also updates the goldItem quantity display
UpdateGoldMsg updateTargetGold = null;
if (tar != null)
updateTargetGold = new UpdateGoldMsg(tar);
else if (corpse != null)
updateTargetGold = new UpdateGoldMsg(corpse);
updateTargetGold.configure();
DispatchMessage.dispatchMsgToInterestArea(tar, updateTargetGold, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
UpdateGoldMsg ugm = new UpdateGoldMsg(player);
ugm.configure();
Dispatch dispatch = Dispatch.borrow(player, ugm);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
// respond back loot message. Try sending to everyone.
} else {
msg.setSourceType1(0);
msg.setSourceType2(0);
msg.setSourceID1(0);
msg.setSourceID2(0);
Dispatch dispatch = Dispatch.borrow(player, msg);
//DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
DispatchMessage.dispatchMsgToInterestArea(tar, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, true);
LootMsg newItemMsg = new LootMsg(GameObjectType.PlayerCharacter.ordinal(), player.getObjectUUID(), 0, 0, itemRet);
dispatch = Dispatch.borrow(player, newItemMsg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
//player.getCharItemManager().updateInventory();
}
//TODO send group loot message if player is grouped and visible
Group group = GroupManager.getGroup(player);
if (group != null && group.getSplitGold() && (item.template.item_type.equals(engine.Enum.ItemType.GOLD) == false)) {
String name = item.getName();
String text = player.getFirstName() + " has looted " + name + '.';
ChatManager.chatGroupInfoCanSee(player, text);
}
} catch (Exception e) {
Logger.info(e.getMessage());
} finally {
item.lootLock.unlock();
}
}
}
// called when player types /show
private static void show(ShowMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter pc = SessionManager.getPlayerCharacter(origin);
if (pc == null)
return;
int targetType = msg.getTargetType();
AbstractCharacter tar = null;
if (targetType == GameObjectType.PlayerCharacter.ordinal())
tar = PlayerCharacter.getFromCache(msg.getTargetID());
else if (targetType == GameObjectType.NPC.ordinal())
tar = NPC.getFromCache(msg.getTargetID());
else if (targetType == GameObjectType.Mob.ordinal())
tar = Mob.getFromCache(msg.getTargetID());
if (tar == null || !tar.isAlive() || !tar.isActive())
return;
msg.setUnknown01(pc.getLoc());
msg.setUnknown02(pc.getLoc());
msg.setRange01(pc.getRange());
msg.setUnknown03(tar.getLoc());
msg.setUnknown04(tar.getLoc());
msg.setRange01(tar.getRange());
Dispatch dispatch = Dispatch.borrow(pc, msg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
}
private static void ViewResourcesMessage(ViewResourcesMessage msg, ClientConnection origin) throws SQLException {
PlayerCharacter player = SessionManager.getPlayerCharacter(origin);
if (player == null)
return;
Guild guild = player.getGuild();
City city = guild.getOwnedCity();
if (city == null)
return;
Building warehouse = BuildingManager.getBuilding(city.getWarehouseBuildingID());
if (warehouse == null)
return;
ViewResourcesMessage vrm = new ViewResourcesMessage(player);
vrm.setWarehouseBuilding(warehouse);
vrm.setGuild(player.getGuild());
vrm.configure();
Dispatch dispatch = Dispatch.borrow(player, vrm);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
}
private static void randomRoll(RandomMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter source = origin.getPlayerCharacter();
if (source == null || !source.isAlive())
return;
//2 second cooldown on random rolls
long lastRandom = source.getTimeStamp("RandomRoll");
if (System.currentTimeMillis() - lastRandom < 2000)
return;
source.setTimeStamp("RandomRoll", System.currentTimeMillis());
//handle random roll
int max = msg.getMax();
if (max > 0)
msg.setRoll(ThreadLocalRandom.current().nextInt(max) + 1);
else if (max < 0) {
max = 1 - max;
msg.setRoll((ThreadLocalRandom.current().nextInt(max) - max) + 1);
}
msg.setSourceType(source.getObjectType().ordinal());
msg.setSourceID(source.getObjectUUID());
//send to all in range
DispatchMessage.dispatchMsgToInterestArea(source, msg, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, true);
}
//returns true if looted item is goldItem and is split. Otherwise returns false
private static void stuck(ClientConnection origin) {
PlayerCharacter sourcePlayer = origin.getPlayerCharacter();
if (sourcePlayer == null)
return;
if (sourcePlayer.getTimers().containsKey("Stuck"))
return;
StuckJob sj = new StuckJob(sourcePlayer);
JobContainer jc = JobScheduler.getInstance().scheduleJob(sj, 10000); // Convert
ConcurrentHashMap<String, JobContainer> timers = sourcePlayer.getTimers();
if (timers != null) {
if (timers.containsKey("Stuck")) {
timers.get("Stuck").cancelJob();
timers.remove("Stuck");
}
timers.put("Stuck", jc);
}
}
private static void GuildTreeStatusMsg(GuildTreeStatusMsg msg, ClientConnection origin) throws SQLException {
PlayerCharacter player = SessionManager.getPlayerCharacter(origin);
Dispatch dispatch;
if (player == null)
return;
if (origin.guildtreespam > System.currentTimeMillis()) {
return;
}
origin.guildtreespam = System.currentTimeMillis() + 5000;
Building b = BuildingManager.getBuildingFromCache(msg.getTargetID());
if (b == null)
return;
GuildTreeStatusMsg gtsm = new GuildTreeStatusMsg(b, player);
gtsm.configure();
dispatch = Dispatch.borrow(player, gtsm);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
}
private static void openSellToNPCWindow(SellToNPCWindowMsg msg, ClientConnection origin) {
PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin);
Dispatch dispatch;
if (sourcePlayer == null)
return;
NPC npc = NPC.getFromCache(msg.getNPCID());
if (npc == null)
return;
// test within talking range
if (sourcePlayer.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) {
ErrorPopupMsg.sendErrorPopup(sourcePlayer, 14);
return;
}
Contract con = npc.getContract();
if (con == null)
return;
float bargain = sourcePlayer.getBargain();
float profit = npc.getBuyPercent(sourcePlayer) + bargain;
if (profit > 1)
profit = 1;
msg.setupOutput();
msg.setUnknown05(profit);
msg.setUnknown06(500000); //TODO set goldItem on npc later
msg.setItemType(con.getBuyItemType());
msg.setSkillTokens(con.getBuySkillToken());
msg.setUnknownArray(con.getBuyUnknownToken());
dispatch = Dispatch.borrow(sourcePlayer, msg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
}
private static void openBuyFromNPCWindow(BuyFromNPCWindowMsg msg, ClientConnection origin) {
PlayerCharacter sourcePlayer = SessionManager.getPlayerCharacter(origin);
Dispatch dispatch;
if (sourcePlayer == null)
return;
NPC npc = NPC.getFromCache(msg.getNpcID());
if (npc == null)
return;
// test within talking range
if (sourcePlayer.getLoc().distanceSquared2D(npc.getLoc()) > MBServerStatics.NPC_TALK_RANGE * MBServerStatics.NPC_TALK_RANGE) {
ErrorPopupMsg.sendErrorPopup(sourcePlayer, 14);
return;
}
dispatch = Dispatch.borrow(sourcePlayer, msg);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
}
protected static void petAttack(PetAttackMsg msg, ClientConnection conn) throws MsgSendException {
PlayerCharacter pc = SessionManager.getPlayerCharacter(conn);
if (pc == null)
return;
Mob pet = pc.getPet();
if (pet == null)
return;
if (!pet.isAlive())
return;
if ((pc.inSafeZone())
&& (msg.getTargetType() == GameObjectType.PlayerCharacter.ordinal()))
return;
//CombatManager.setAttackTarget(msg, conn);
if (msg.getTargetType() == GameObjectType.Building.ordinal()) {
conn.getPlayerCharacter().getPet().setCombatTarget(PlayerCharacter.getPlayerCharacter(msg.getTargetID()));
}
switch (msg.getTargetType()) {
case 53: //player character
conn.getPlayerCharacter().getPet().setCombatTarget(PlayerCharacter.getPlayerCharacter(msg.getTargetID()));
break;
case 37://mob
conn.getPlayerCharacter().getPet().setCombatTarget(Mob.getMob(msg.getTargetID()));
break;
case 8://mob
conn.getPlayerCharacter().getPet().setCombatTarget(BuildingManager.getBuilding(msg.getTargetID()));
break;
}
if (pet.getCombatTarget() == null)
return;
}
protected static void petCmd(PetCmdMsg msg, ClientConnection conn) throws MsgSendException {
PlayerCharacter pc = SessionManager.getPlayerCharacter(conn);
if (pc == null)
return;
Mob pet = pc.getPet();
if (pet == null)
return;
if (!pet.isAlive())
return;
//if (pet.state == STATE.Disabled)
// return;
int type = msg.getType();
if (type == 1) { //stop attack
pet.setCombatTarget(null);
pc.setCombat(false);
} else if (type == 2) { //dismiss
pet.dismiss();
pc.dismissPet();
if (pet.isAlive())
WorldGrid.updateObject(pet);
} else if (type == 3) //toggle assist
pet.toggleAssist();
else if (type == 5) { //rest
boolean sit = (!(pet.isSit()));
pet.setSit(sit);
// cancel effects that break on sit
if (pet.isSit())
pet.cancelOnSit();
UpdateStateMsg rwss = new UpdateStateMsg();
rwss.setPlayer(pet);
DispatchMessage.sendToAllInRange(pet, rwss);
}
}
//Handle RepairObject Window and RepairObject Requests
protected static void HandlePromptRecall(PromptRecallMsg msg, ClientConnection origin) throws MsgSendException {
PlayerCharacter player = SessionManager.getPlayerCharacter(origin);
boolean recallAccepted;
if (player == null)
return;
boolean confirmed = msg.getConfirmed();
if (confirmed == true) {
long timeElapsed = System.currentTimeMillis() - player.getTimeStamp("PromptRecall");
//send fail message
recallAccepted = timeElapsed < 15000;
} else
recallAccepted = false;
if (recallAccepted == true) {
//handle recall
long type = player.getTimeStamp("LastRecallType");
if (type == 1) { //recall to bind
player.teleport(player.getBindLoc());
player.setSafeMode();
} else { //recall to rg
float dist = 9999999999f;
Building rg = null;
Vector3fImmutable rgLoc;
for (Runegate runegate : Runegate._runegates.values()) {
rgLoc = runegate.gateBuilding.getLoc();
float distanceSquaredToRunegate = player.getLoc().distanceSquared2D(rgLoc);
if (distanceSquaredToRunegate < sqr(dist))
rg = runegate.gateBuilding;
}
//nearest runegate found. teleport characterTarget
if (rg != null) {
player.teleport(rg.getLoc());
player.setSafeMode();
}
}
}
}
@Override
public boolean handleClientMsg(ClientNetMsg msg) {
if (msg == null) {
Logger.error("handleClientMsg", "Recieved null msg. Returning.");
return false;
}
ClientConnection origin;
Protocol protocolMsg = Protocol.NONE;
Session s;
try {
// Try registered opcodes first as we take a hatchet to this GodObject
AbstractClientMsgHandler msgHandler = msg.getProtocolMsg().handler;
if (msgHandler != null)
return msgHandler.handleNetMsg(msg);
// Any remaining opcodes fall through and are routed
// through this ungodly switch of doom.
origin = (ClientConnection) msg.getOrigin();
s = SessionManager.getSession(origin);
protocolMsg = msg.getProtocolMsg();
switch (protocolMsg) {
case SETSELECTEDOBECT:
ClientMessagePump.targetObject((TargetObjectMsg) msg, origin);
break;
/*
* Chat
*/
// Simplify by fall through. Route in ChatManager
case CHATSAY:
case CHATSHOUT:
case CHATTELL:
case CHATGUILD:
case CHATGROUP:
case CHATPVP:
case CHATIC:
case CHATCITY:
case CHATINFO:
case SYSTEMBROADCASTCHANNEL:
case CHATCSR:
case SYSTEMCHANNEL:
case GLOBALCHANNELMESSAGE:
case LEADERCHANNELMESSAGE:
ChatManager.handleChatMsg(s, (AbstractChatMsg) msg);
break;
case UPDATESTATE:
UpdateStateMsg rwss = (UpdateStateMsg) msg;
runWalkSitStand(rwss, origin);
break;
case ACTIVATECHARTER:
UseCharterMsg ucm = (UseCharterMsg) msg;
ucm.setUnknown02(1);
ucm.configure();
Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), ucm);
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
break;
case CHECKUNIQUEGUILD:
break;
case CANCELGUILDCREATION:
break;
case LEAVEREQUEST:
origin.disconnect();
break;
case POWER:
PowersManager.usePower((PerformActionMsg) msg, origin, false);
break;
case READYTOENTER:
break;
case OPENVAULT:
break;
case WHOREQUEST:
WhoRequest((WhoRequestMsg) msg, origin);
break;
case CLIENTADMINCOMMAND:
ChatManager.HandleClientAdminCmd((ClientAdminCommandMsg) msg, origin);
break;
case SOCIALCHANNEL:
social((SocialMsg) msg, origin);
break;
case COMBATMODE:
CombatManager.toggleCombat(((ToggleCombatMsg) msg).toggleCombat(), origin);
break;
case ARCCOMBATMODEATTACKING:
CombatManager.toggleCombat(((SetCombatModeMsg) msg).getToggle(), origin);
break;
case MODIFYGUILDSTATE:
ToggleLfgRecruitingMsg tlrm = (ToggleLfgRecruitingMsg) msg;
toggleLfgRecruiting(tlrm, origin);
break;
case TOGGLESITSTAND:
ToggleSitStandMsg tssm = (ToggleSitStandMsg) msg;
toggleSitStand(tssm, origin);
break;
case GUILDTREESTATUS:
GuildTreeStatusMsg((GuildTreeStatusMsg) msg, origin);
break;
case IGNORE:
((IgnoreMsg) msg).handleRequest(origin);
break;
case DELETEOBJECT:
DeleteItem((DeleteItemMsg) msg, origin);
break;
case VIEWRESOURCES:
ViewResourcesMessage((ViewResourcesMessage) msg, origin);
break;
case RAISEATTR:
modifyStat((ModifyStatMsg) msg, origin);
break;
case COSTTOOPENBANK:
ackBankWindowOpened((AckBankWindowOpenedMsg) msg, origin);
break;
case RESETAFTERDEATH:
respawn((RespawnMsg) msg, origin);
break;
case REQUESTCONTENTS:
lootWindowRequest((LootWindowRequestMsg) msg, origin);
break;
case MOVEOBJECTTOCONTAINER:
loot((LootMsg) msg, origin);
break;
case SHOWCOMBATINFO:
show((ShowMsg) msg, origin);
break;
case REQUESTTOTRADE:
TradeManager.tradeRequest((TradeRequestMsg) msg, origin);
break;
case REQUESTTRADEOK:
TradeManager.acceptTradeRequest((AcceptTradeRequestMsg) msg, origin);
break;
case REQUESTTRADECANCEL:
TradeManager.rejectTradeRequest((RejectTradeRequestMsg) msg, origin);
break;
case TRADEADDOBJECT:
TradeManager.addItemToTradeWindow((AddItemToTradeWindowMsg) msg, origin);
break;
case TRADEADDGOLD:
TradeManager.addGoldToTradeWindow((AddGoldToTradeWindowMsg) msg, origin);
break;
case TRADECONFIRM:
TradeManager.commitToTrade((CommitToTradeMsg) msg, origin);
break;
case TRADEUNCONFIRM:
TradeManager.uncommitToTrade((UncommitToTradeMsg) msg, origin);
break;
case TRADECLOSE:
TradeManager.closeTradeWindow((CloseTradeWindowMsg) msg, origin);
break;
case ARCREQUESTTRADEBUSY:
TradeManager.invalidTradeRequest((InvalidTradeRequestMsg) msg);
break;
case VENDORDIALOG:
VendorDialogMsg.replyDialog((VendorDialogMsg) msg, origin);
break;
case SHOPLIST:
openBuyFromNPCWindow((BuyFromNPCWindowMsg) msg, origin);
break;
case SHOPINFO:
openSellToNPCWindow((SellToNPCWindowMsg) msg, origin);
break;
case TRAINERLIST:
WorldServer.trainerInfo((TrainerInfoMsg) msg, origin);
break;
case ARCUNTRAINLIST:
WorldServer.refinerScreen((RefinerScreenMsg) msg, origin);
break;
case TRAINSKILL:
TrainMsg.train((TrainMsg) msg, origin);
break;
case ARCUNTRAINABILITY:
RefineMsg.refine((RefineMsg) msg, origin);
break;
case POWERTARGNAME:
PowersManager.summon((SendSummonsRequestMsg) msg, origin);
break;
case ARCSUMMON:
PowersManager.recvSummon((RecvSummonsRequestMsg) msg, origin);
break;
case ARCTRACKINGLIST:
PowersManager.trackWindow((TrackWindowMsg) msg, origin);
break;
case STUCK:
stuck(origin);
break;
case RANDOM:
ClientMessagePump.randomRoll((RandomMsg) msg, origin);
break;
case ARCPETATTACK:
petAttack((PetAttackMsg) msg, origin);
break;
case ARCPETCMD:
petCmd((PetCmdMsg) msg, origin);
break;
case MANAGENPC:
ManageNPCCmd((ManageNPCMsg) msg, origin);
break;
case ARCPROMPTRECALL:
HandlePromptRecall((PromptRecallMsg) msg, origin);
break;
case CHANNELMUTE:
break;
case KEEPALIVESERVERCLIENT:
break;
case UNKNOWN:
break;
case CONFIRMPROMOTE:
break;
default:
String ocHex = StringUtils.toHexString(protocolMsg.opcode);
Logger.error("Cannot handle Opcode: " + ocHex + " " + protocolMsg.name());
return false;
}
} catch (MsgSendException | SQLException e) {
Logger.error("handler for " + protocolMsg + " failed: " + e);
return false;
}
return true;
}
//TODO what is this used for?
private void ManageNPCCmd(ManageNPCMsg msg, ClientConnection origin) {
}
//call this if the transfer fails server side to kick the item back to inventory from equip
private void forceTransferFromInventoryToEquip(TransferItemFromEquipToInventoryMsg msg, ClientConnection origin, String reason) {
//TODO add this later
//PATCHED CODEZZ
}
}