diff --git a/src/main/java/client/creator/CharacterFactory.java b/src/main/java/client/creator/CharacterFactory.java index 1b26d1dc48..920fc891a9 100644 --- a/src/main/java/client/creator/CharacterFactory.java +++ b/src/main/java/client/creator/CharacterFactory.java @@ -47,19 +47,19 @@ public abstract class CharacterFactory { return -1; } - Character newchar = Character.getDefault(c); - newchar.setWorld(c.getWorld()); - newchar.setSkinColor(SkinColor.getById(skin)); - newchar.setGender(gender); - newchar.setName(name); - newchar.setHair(hair); - newchar.setFace(face); + Character newCharacter = Character.getDefault(c); + newCharacter.setWorld(c.getWorld()); + newCharacter.setSkinColor(SkinColor.getById(skin)); + newCharacter.setGender(gender); + newCharacter.setName(name); + newCharacter.setHair(hair); + newCharacter.setFace(face); - newchar.setLevel(recipe.getLevel()); - newchar.setJob(recipe.getJob()); - newchar.setMapId(recipe.getMap()); + newCharacter.setLevel(recipe.getLevel()); + newCharacter.setJob(recipe.getJob()); + newCharacter.setMapId(recipe.getMap()); - Inventory equipped = newchar.getInventory(InventoryType.EQUIPPED); + Inventory equipped = newCharacter.getInventory(InventoryType.EQUIPPED); ItemInformationProvider ii = ItemInformationProvider.getInstance(); int top = recipe.getTop(), bottom = recipe.getBottom(), shoes = recipe.getShoes(), weapon = recipe.getWeapon(); @@ -88,12 +88,17 @@ public abstract class CharacterFactory { equipped.addItemFromDB(eq_weapon.copy()); } - if (!newchar.insertNewChar(recipe)) { + if (!MakeCharInfoValidator.isNewCharacterValid(newCharacter)) { + log.warn("Owner from account {} tried to packet edit in character creation", c.getAccountName()); return -2; } - c.sendPacket(PacketCreator.addNewCharEntry(newchar)); - Server.getInstance().createCharacterEntry(newchar); + if (!newCharacter.insertNewChar(recipe)) { + return -2; + } + c.sendPacket(PacketCreator.addNewCharEntry(newCharacter)); + + Server.getInstance().createCharacterEntry(newCharacter); Server.getInstance().broadcastGMMessage(c.getWorld(), PacketCreator.sendYellowTip("[New Char]: " + c.getAccountName() + " has created a new character with IGN " + name)); log.info("Account {} created chr with name {}", c.getAccountName(), name); diff --git a/src/main/java/client/creator/MakeCharInfo.java b/src/main/java/client/creator/MakeCharInfo.java new file mode 100644 index 0000000000..5447c958ea --- /dev/null +++ b/src/main/java/client/creator/MakeCharInfo.java @@ -0,0 +1,140 @@ +package client.creator; + +import client.Character; +import client.Job; +import client.inventory.InventoryType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import provider.Data; +import provider.DataTool; + +import java.util.HashSet; +import java.util.Set; + +public class MakeCharInfo { + private static final Logger log = LoggerFactory.getLogger(MakeCharInfo.class); + private static final String FACE_ID = "0"; + private static final String HAIR_ID = "1"; + private static final String HAIR_COLOR_ID = "2"; + private static final String SKIN_ID = "3"; + private static final String TOP_ID = "4"; + private static final String BOTTOM_ID = "5"; + private static final String SHOE_ID = "6"; + private static final String WEAPON_ID = "7"; + + private final Set charFaces = new HashSet<>(); + private final Set charHairs = new HashSet<>(); + private final Set charHairColors = new HashSet<>(); + private final Set charSkins = new HashSet<>(); + private final Set charTops = new HashSet<>(); + private final Set charBottoms = new HashSet<>(); + private final Set charShoes = new HashSet<>(); + private final Set charWeapons = new HashSet<>(); + + public MakeCharInfo(Data charInfoData) { + for (Data data : charInfoData.getChildren()) { + switch (data.getName()) { + case FACE_ID -> { + for (Data faceData : data) { + charFaces.add(DataTool.getInt(faceData)); + } + } + case HAIR_ID -> { + for (Data hairData : data) { + charHairs.add(DataTool.getInt(hairData)); + } + } + case HAIR_COLOR_ID -> { + for (Data hairColorData : data) { + charHairColors.add(DataTool.getInt(hairColorData)); + } + } + case SKIN_ID -> { + for (Data skinData : data) { + charSkins.add(DataTool.getInt(skinData)); + } + } + case TOP_ID -> { + for (Data topData : data) { + charTops.add(DataTool.getInt(topData)); + } + } + case BOTTOM_ID -> { + for (Data bottomData : data) { + charBottoms.add(DataTool.getInt(bottomData)); + } + } + case SHOE_ID -> { + for (Data shoeData : data) { + charShoes.add(DataTool.getInt(shoeData)); + } + } + case WEAPON_ID -> { + for (Data weaponData : data) { + charWeapons.add(DataTool.getInt(weaponData)); + } + } + default -> log.error("Unhandled node inside MakeCharInfo.img.xml: '" + data.getName() + "'"); + } + } + } + + public boolean verifyFaceId(int id) { + return this.charFaces.contains(id); + } + + public boolean verifyHairId(int id) { + if (id % 10 != 0) { + return this.charHairs.contains(id - (id % 10)); + } + return this.charHairs.contains(id); + } + + public boolean verifyHairColorId(int id) { + return this.charHairColors.contains(id % 10); + } + + public boolean verifySkinId(int id) { + return this.charSkins.contains(id); + } + + public boolean verifyTopId(int id) { + return this.charTops.contains(id); + } + + public boolean verifyBottomId(int id) { + return this.charBottoms.contains(id); + } + + public boolean verifyShoeId(int id) { + return this.charShoes.contains(id); + } + + public boolean verifyWeaponId(int id) { + return this.charWeapons.contains(id); + } + + public boolean verifyCharacter(Character character) { + if (!verifyFaceId(character.getFace())) return false; + if (!verifyHairId(character.getHair())) return false; + if (!verifyHairColorId(character.getHair())) return false; + if (!verifySkinId(character.getSkinColor().getId())) return false; + + // Here we only verify the equipment if the character that's being created is of type 'Beginner' + // This is because when the Maple Life A or Maple Life B items are used, the client does not send any data + // regarding what equipment the character should be wearing (as it's all handled server-side) + Job characterJob = character.getJob(); + if (characterJob == Job.BEGINNER || characterJob == Job.NOBLESSE || characterJob == Job.LEGEND) { + if (!verifyTopId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -5).getItemId())) + return false; + if (!verifyBottomId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -6).getItemId())) + return false; + if (!verifyShoeId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -7).getItemId())) + return false; + if (!verifyWeaponId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -11).getItemId())) + return false; + } + + return true; + } +} diff --git a/src/main/java/client/creator/MakeCharInfoValidator.java b/src/main/java/client/creator/MakeCharInfoValidator.java new file mode 100644 index 0000000000..4ae333e8a3 --- /dev/null +++ b/src/main/java/client/creator/MakeCharInfoValidator.java @@ -0,0 +1,41 @@ +package client.creator; + +import client.Character; +import provider.Data; +import provider.DataProviderFactory; +import provider.wz.WZFiles; + +public class MakeCharInfoValidator { + private static final MakeCharInfo charFemale; + private static final MakeCharInfo charMale; + private static final MakeCharInfo orientCharFemale; + private static final MakeCharInfo orientCharMale; + private static final MakeCharInfo premiumCharFemale; + private static final MakeCharInfo premiumCharMale; + + static { + Data data = DataProviderFactory.getDataProvider(WZFiles.ETC).getData("MakeCharInfo.img"); + charFemale = new MakeCharInfo(data.getChildByPath("Info/CharFemale")); + charMale = new MakeCharInfo(data.getChildByPath("Info/CharMale")); + orientCharFemale = new MakeCharInfo(data.getChildByPath("OrientCharFemale")); + orientCharMale = new MakeCharInfo(data.getChildByPath("OrientCharMale")); + premiumCharFemale = new MakeCharInfo(data.getChildByPath("PremiumCharFemale")); + premiumCharMale = new MakeCharInfo(data.getChildByPath("PremiumCharMale")); + } + + private static MakeCharInfo getMakeCharInfo(Character character) { + return switch (character.getJob()) { + case BEGINNER, WARRIOR, MAGICIAN, BOWMAN, THIEF, PIRATE -> character.isMale() ? charMale : charFemale; + case NOBLESSE -> character.isMale() ? premiumCharMale : premiumCharFemale; + case LEGEND -> character.isMale() ? orientCharMale : orientCharFemale; + default -> null; + }; + } + + public static boolean isNewCharacterValid(Character character) { + MakeCharInfo makeCharInfo = getMakeCharInfo(character); + if (makeCharInfo == null) return false; + + return makeCharInfo.verifyCharacter(character); + } +} diff --git a/src/main/java/client/creator/novice/BeginnerCreator.java b/src/main/java/client/creator/novice/BeginnerCreator.java index 5271025bbf..986eb66fda 100644 --- a/src/main/java/client/creator/novice/BeginnerCreator.java +++ b/src/main/java/client/creator/novice/BeginnerCreator.java @@ -43,7 +43,6 @@ public class BeginnerCreator extends CharacterFactory { } public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) { - int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.BEGINNER, 1, MapId.MUSHROOM_TOWN, top, bottom, shoes, weapon)); - return status; + return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.BEGINNER, 1, MapId.MUSHROOM_TOWN, top, bottom, shoes, weapon)); } } diff --git a/src/main/java/client/creator/novice/LegendCreator.java b/src/main/java/client/creator/novice/LegendCreator.java index 75c5226c5a..638bc5a8cd 100644 --- a/src/main/java/client/creator/novice/LegendCreator.java +++ b/src/main/java/client/creator/novice/LegendCreator.java @@ -43,7 +43,6 @@ public class LegendCreator extends CharacterFactory { } public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) { - int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.LEGEND, 1, MapId.ARAN_TUTORIAL_START, top, bottom, shoes, weapon)); - return status; + return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.LEGEND, 1, MapId.ARAN_TUTORIAL_START, top, bottom, shoes, weapon)); } } diff --git a/src/main/java/client/creator/novice/NoblesseCreator.java b/src/main/java/client/creator/novice/NoblesseCreator.java index 2c51e7c898..cb04459622 100644 --- a/src/main/java/client/creator/novice/NoblesseCreator.java +++ b/src/main/java/client/creator/novice/NoblesseCreator.java @@ -43,7 +43,6 @@ public class NoblesseCreator extends CharacterFactory { } public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) { - int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.NOBLESSE, 1, MapId.STARTING_MAP_NOBLESSE, top, bottom, shoes, weapon)); - return status; + return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.NOBLESSE, 1, MapId.STARTING_MAP_NOBLESSE, top, bottom, shoes, weapon)); } } diff --git a/src/main/java/constants/id/ItemId.java b/src/main/java/constants/id/ItemId.java index 95ad8f2ec7..2624966f0b 100644 --- a/src/main/java/constants/id/ItemId.java +++ b/src/main/java/constants/id/ItemId.java @@ -95,46 +95,6 @@ public class ItemId { public static final int BEGINNERS_GUIDE = 4161001; public static final int LEGENDS_GUIDE = 4161048; public static final int NOBLESSE_GUIDE = 4161047; - public static final int SWORD = 1302000; // Weapon - public static final int HAND_AXE = 1312004; - public static final int WOODEN_CLUB = 1322005; - public static final int BASIC_POLEARM = 1442079; - public static final int WHITE_UNDERSHIRT = 1040002; // Top - public static final int UNDERSHIRT = 1040006; - public static final int GREY_TSHIRT = 1040010; - public static final int WHITE_TUBETOP = 1041002; - public static final int YELLOW_TSHIRT = 1041006; - public static final int GREEN_TSHIRT = 1041010; - public static final int RED_STRIPED_TOP = 1041011; - public static final int SIMPLE_WARRIOR_TOP = 1042167; - public static final int BLUE_JEAN_SHORTS = 1060002; // Bottom - public static final int BROWN_COTTON_SHORTS = 1060006; - public static final int RED_MINISKIRT = 1061002; - public static final int INDIGO_MINISKIRT = 1061008; - public static final int SIMPLE_WARRIOR_PANTS = 1062115; - public static final int RED_RUBBER_BOOTS = 1072001; - public static final int LEATHER_SANDALS = 1072005; - public static final int YELLOW_RUBBER_BOOTS = 1072037; - public static final int BLUE_RUBBER_BOOTS = 1072038; - public static final int AVERAGE_MUSASHI_SHOES = 1072383; - public static final int BLACK_TOBEN = 30000; // Hair - public static final int ZETA = 30010; - public static final int BLACK_REBEL = 30020; - public static final int BLACK_BUZZ = 30030; - public static final int BLACK_SAMMY = 31000; - public static final int BLACK_EDGY = 31040; - public static final int BLACK_CONNIE = 31050; - public static final int MOTIVATED_LOOK_M = 20000; // Face - public static final int PERPLEXED_STARE = 20001; - public static final int LEISURE_LOOK_M = 20002; - public static final int MOTIVATED_LOOK_F = 21000; - public static final int FEARFUL_STARE_M = 21001; - public static final int LEISURE_LOOK_F = 21002; - public static final int FEARFUL_STARE_F = 21201; - public static final int PERPLEXED_STARE_HAZEL = 20401; - public static final int LEISURE_LOOK_HAZEL = 20402; - public static final int MOTIVATED_LOOK_AMETHYST = 21700; - public static final int MOTIVATED_LOOK_BLUE = 20100; // Warrior public static final int RED_HWARANG_SHIRT = 1040021; diff --git a/src/main/java/net/server/handlers/login/CreateCharHandler.java b/src/main/java/net/server/handlers/login/CreateCharHandler.java index 9a62af04ed..853e08e67a 100644 --- a/src/main/java/net/server/handlers/login/CreateCharHandler.java +++ b/src/main/java/net/server/handlers/login/CreateCharHandler.java @@ -25,43 +25,14 @@ import client.Client; import client.creator.novice.BeginnerCreator; import client.creator.novice.LegendCreator; import client.creator.novice.NoblesseCreator; -import constants.id.ItemId; import net.AbstractPacketHandler; import net.packet.InPacket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import tools.PacketCreator; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - public final class CreateCharHandler extends AbstractPacketHandler { - private static final Logger log = LoggerFactory.getLogger(CreateCharHandler.class); - - private final static Set IDs = new HashSet<>(Arrays.asList( - ItemId.SWORD, ItemId.HAND_AXE, ItemId.WOODEN_CLUB, ItemId.BASIC_POLEARM,// weapons - ItemId.WHITE_UNDERSHIRT, ItemId.UNDERSHIRT, ItemId.GREY_TSHIRT, ItemId.WHITE_TUBETOP, ItemId.YELLOW_TSHIRT, - ItemId.GREEN_TSHIRT, ItemId.RED_STRIPED_TOP, ItemId.SIMPLE_WARRIOR_TOP,// bottom - ItemId.BLUE_JEAN_SHORTS, ItemId.BROWN_COTTON_SHORTS, ItemId.RED_MINISKIRT, ItemId.INDIGO_MINISKIRT, - ItemId.SIMPLE_WARRIOR_PANTS, // top - ItemId.RED_RUBBER_BOOTS, ItemId.LEATHER_SANDALS, ItemId.YELLOW_RUBBER_BOOTS, ItemId.BLUE_RUBBER_BOOTS, - ItemId.AVERAGE_MUSASHI_SHOES,// shoes - ItemId.BLACK_TOBEN, ItemId.ZETA, ItemId.BLACK_REBEL, ItemId.BLACK_BUZZ, ItemId.BLACK_SAMMY, - ItemId.BLACK_EDGY, ItemId.BLACK_CONNIE,// hair - ItemId.MOTIVATED_LOOK_M, ItemId.PERPLEXED_STARE, ItemId.LEISURE_LOOK_M, ItemId.MOTIVATED_LOOK_F, - ItemId.FEARFUL_STARE_M, ItemId.LEISURE_LOOK_F, ItemId.FEARFUL_STARE_F, ItemId.PERPLEXED_STARE_HAZEL, - ItemId.LEISURE_LOOK_HAZEL, ItemId.MOTIVATED_LOOK_AMETHYST, ItemId.MOTIVATED_LOOK_BLUE //face - //#NeverTrustStevenCode - )); - - private static boolean isLegal(Integer toCompare) { - return IDs.contains(toCompare); - } - @Override - public final void handlePacket(InPacket p, Client c) { + public void handlePacket(InPacket p, Client c) { String name = p.readString(); int job = p.readInt(); int face = p.readInt(); @@ -76,15 +47,6 @@ public final class CreateCharHandler extends AbstractPacketHandler { int weapon = p.readInt(); int gender = p.readByte(); - int[] items = new int[]{weapon, top, bottom, shoes, hair, face}; - for (int item : items) { - if (!isLegal(item)) { - log.warn("Owner from account {} tried to packet edit in chr creation", c.getAccountName()); - c.disconnect(true, false); - return; - } - } - int status; switch (job) { case 0: // Knights of Cygnus