/******************************************************************************* * Copyright (c) 2013 * * This File is part of NGECore2. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * * Using NGEngine to work with NGECore2 is making a combined work based on NGEngine. * Therefore all terms and conditions of the GNU Lesser General Public License cover the combination. ******************************************************************************/ package services; import java.io.IOException; import java.nio.ByteOrder; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; import main.NGECore; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import engine.clients.Client; import engine.resources.common.CRC; import engine.resources.container.CreatureContainerPermissions; import engine.resources.container.CreaturePermissions; import engine.resources.database.DatabaseConnection; import engine.resources.scene.Point3D; import engine.resources.scene.Quaternion; import engine.resources.service.INetworkDispatch; import engine.resources.service.INetworkRemoteEvent; import resources.common.*; import protocol.swg.ClientCreateCharacter; import protocol.swg.ClientMfdStatusUpdateMessage; import protocol.swg.ClientRandomNameRequest; import protocol.swg.ClientRandomNameResponse; import protocol.swg.ClientVerifyAndLockNameRequest; import protocol.swg.ClientVerifyAndLockNameResponse; import protocol.swg.CreateCharacterSuccess; import protocol.swg.HeartBeatMessage; import resources.objects.creature.CreatureObject; import resources.objects.player.PlayerObject; import resources.objects.tangible.TangibleObject; import resources.objects.weapon.WeaponObject; @SuppressWarnings("unused") public class CharacterService implements INetworkDispatch { private NGECore core; private DatabaseConnection databaseConnection; private DatabaseConnection databaseConnection2; private engine.resources.common.NameGen nameGenerator; private static final String allowedCharsRegex = "['-]?[A-Za-z]('[a-zA-Z]|-[a-zA-Z]|[a-zA-Z])*['-]?$"; public CharacterService(NGECore core) { this.core = core; this.databaseConnection = core.getDatabase1(); this.databaseConnection2 = core.getDatabase2(); try { nameGenerator = new engine.resources.common.NameGen("names.txt"); } catch (IOException e) { e.printStackTrace(); } } @Override public void insertOpcodes(Map swgOpcodes, Map objControllerOpcodes) { swgOpcodes.put(Opcodes.ClientRandomNameRequest, new INetworkRemoteEvent() { public void handlePacket(IoSession session, IoBuffer data) throws Exception { Client client = core.getClient((Integer) session.getAttribute("connectionId")); ClientRandomNameRequest randomNameRequest = new ClientRandomNameRequest(); ClientRandomNameResponse response; String name = null; data = data.order(ByteOrder.LITTLE_ENDIAN); data.position(0); randomNameRequest.deserialize(data); while (name == null) { if (randomNameRequest.getSharedRaceTemplate().contains("wookie")) { name = nameGenerator.compose(4); } else { name = nameGenerator.compose(2) + " " + nameGenerator.compose(3); } try { if (checkForDuplicateName(getfirstName(name, randomNameRequest.getSharedRaceTemplate()), client.getAccountId())) { name = null; } } catch (SQLException e2) { e2.printStackTrace(); } } response = new ClientRandomNameResponse(randomNameRequest.getSharedRaceTemplate(), name); session.write(response.serialize()); } }); swgOpcodes.put(Opcodes.ClientVerifyAndLockNameRequest, new INetworkRemoteEvent() { @Override public void handlePacket(IoSession session, IoBuffer data) throws Exception { Client client = core.getClient((Integer) session.getAttribute("connectionId")); data = data.order(ByteOrder.LITTLE_ENDIAN); data.position(0); ClientVerifyAndLockNameRequest message = new ClientVerifyAndLockNameRequest(); message.deserialize(data); String approved_flag = "name_approved"; boolean isDeclined = false; String firstName = getfirstName(message.getName(), message.getRaceTemplate()); String lastName = getlastName(message.getName(), message.getRaceTemplate()); if(message.getName() != null) System.out.println(message.getName()); int length = firstName.length(); try { if (checkForDuplicateName(firstName, client.getAccountId())) { approved_flag = "name_declined_in_use"; isDeclined = true; } if ((!isDeclined) && (length < 3 || length > 15 || lastName.length() > 20)) { approved_flag = "name_declined_racially_inappropriate"; isDeclined = true; } if (!isDeclined) { String rv = checkForReservedName(firstName, lastName); if (rv != null) { approved_flag = rv; isDeclined = true; } } if (!isDeclined) { if (!firstName.matches(allowedCharsRegex) || (!lastName.matches(allowedCharsRegex) && lastName != "")) { approved_flag = "name_declined_syntax"; isDeclined = true; } } if (!isDeclined) { approved_flag = "name_approved"; } } catch (SQLException e) { e.printStackTrace(); } ClientVerifyAndLockNameResponse response = new ClientVerifyAndLockNameResponse(firstName, approved_flag); session.write(response.serialize()); } }); swgOpcodes.put(Opcodes.ClientCreateCharacter, new INetworkRemoteEvent() { @Override public void handlePacket(IoSession session, IoBuffer data) throws Exception { data = data.order(ByteOrder.LITTLE_ENDIAN); data.position(0); ClientCreateCharacter clientCreateCharacter = new ClientCreateCharacter(); clientCreateCharacter.deserialize(data); engine.resources.config.Config config = new engine.resources.config.Config(); config.setFilePath("nge.cfg"); if (!(config.loadConfigFile())) { config = engine.resources.config.DefaultConfig.getConfig(); } int galaxyId = config.getInt("GALAXY_ID"); Client client = core.getClient((Integer) session.getAttribute("connectionId")); try { if (checkForDuplicateName(getfirstName(clientCreateCharacter.getName(), clientCreateCharacter.getRaceTemplate()), client.getAccountId())) { return; } } catch (SQLException e2) { e2.printStackTrace(); } // TODO: Add starting location and items in a script String raceTemplate = clientCreateCharacter.getRaceTemplate(); // should set to some planet and starting location String sharedRaceTemplate = raceTemplate.replace("player/", "player/shared_"); CreatureObject object = (CreatureObject) core.objectService.createObject(sharedRaceTemplate, core.terrainService.getPlanetByName("tatooine")); object.setContainerPermissions(CreaturePermissions.CREATURE_PERMISSIONS); object.setCustomization(clientCreateCharacter.getCustomizationData()); object.setCustomName(clientCreateCharacter.getName()); object.setHeight(clientCreateCharacter.getScale()); object.setPersistent(true); object.setPosition(new Point3D(3528, 0, -4804)); object.setCashCredits(100); object.setBankCredits(1000); //object.setPosition(new Point3D(0, 0, 0)); object.setOrientation(new Quaternion(1, 0, 0, 0)); float luck = (((((float) (core.scriptService.getMethod("scripts/roadmap/", clientCreateCharacter.getProfession(), "getLuck").__call__().asInt()) + (core.scriptService.getMethod("scripts/roadmap/", object.getStfName(), "getLuck").__call__().asInt())) / ((float) 90)) * ((float) object.getLevel())) - ((float) object.getSkillModBase("luck"))); float precision = (((((float) (core.scriptService.getMethod("scripts/roadmap/", clientCreateCharacter.getProfession(), "getPrecision").__call__().asInt()) + (core.scriptService.getMethod("scripts/roadmap/", object.getStfName(), "getPrecision").__call__().asInt())) / ((float) 90)) * ((float) object.getLevel())) - ((float) object.getSkillModBase("precision"))); float strength = (((((float) (core.scriptService.getMethod("scripts/roadmap/", clientCreateCharacter.getProfession(), "getStrength").__call__().asInt()) + (core.scriptService.getMethod("scripts/roadmap/", object.getStfName(), "getStrength").__call__().asInt())) / ((float) 90)) * ((float) object.getLevel())) - ((float) object.getSkillModBase("strength"))); float constitution = (((((float) (core.scriptService.getMethod("scripts/roadmap/", clientCreateCharacter.getProfession(), "getConstitution").__call__().asInt()) + (core.scriptService.getMethod("scripts/roadmap/", object.getStfName(), "getConstitution").__call__().asInt())) / ((float) 90)) * ((float) object.getLevel())) - ((float) object.getSkillModBase("constitution"))); float stamina = (((((float) (core.scriptService.getMethod("scripts/roadmap/", clientCreateCharacter.getProfession(), "getStamina").__call__().asInt()) + (core.scriptService.getMethod("scripts/roadmap/", object.getStfName(), "getStamina").__call__().asInt())) / ((float) 90)) * ((float) object.getLevel())) - ((float) object.getSkillModBase("stamina"))); float agility = (((((float) (core.scriptService.getMethod("scripts/roadmap/", clientCreateCharacter.getProfession(), "getAgility").__call__().asInt()) + (core.scriptService.getMethod("scripts/roadmap/", object.getStfName(), "getAgility").__call__().asInt())) / ((float) 90)) * ((float) object.getLevel())) - ((float) object.getSkillModBase("agility"))); if (luck >= 1) core.skillModService.addSkillMod(object, "luck", (int) luck); if (precision >= 1) core.skillModService.addSkillMod(object, "precision", (int) precision); if (strength >= 1) core.skillModService.addSkillMod(object, "strength", (int) strength); if (constitution >= 1) core.skillModService.addSkillMod(object, "constitution", (int) constitution); if (stamina >= 1) core.skillModService.addSkillMod(object, "stamina", (int) stamina); if (agility >= 1) core.skillModService.addSkillMod(object, "agility", (int) agility); object.createTransaction(core.getCreatureODB().getEnvironment()); PlayerObject player = (PlayerObject) core.objectService.createObject("object/player/shared_player.iff", object.getPlanet()); object._add(player); player.setProfession(clientCreateCharacter.getProfession()); player.setProfessionWheelPosition(clientCreateCharacter.getProfessionWheelPosition()); if(clientCreateCharacter.getHairObject().length() > 0) { String sharedHairTemplate = clientCreateCharacter.getHairObject().replace("/hair_", "/shared_hair_"); TangibleObject hair = (TangibleObject) core.objectService.createObject(sharedHairTemplate, object.getPlanet()); if(clientCreateCharacter.getHairCustomization().length > 0) hair.setCustomization(clientCreateCharacter.getHairCustomization()); object._add(hair); } TangibleObject inventory = (TangibleObject) core.objectService.createObject("object/tangible/inventory/shared_character_inventory.iff", object.getPlanet()); inventory.setContainerPermissions(CreatureContainerPermissions.CREATURE_CONTAINER_PERMISSIONS); TangibleObject appInventory = (TangibleObject) core.objectService.createObject("object/tangible/inventory/shared_appearance_inventory.iff", object.getPlanet()); appInventory.setContainerPermissions(CreaturePermissions.CREATURE_PERMISSIONS); TangibleObject datapad = (TangibleObject) core.objectService.createObject("object/tangible/datapad/shared_character_datapad.iff", object.getPlanet()); datapad.setContainerPermissions(CreatureContainerPermissions.CREATURE_CONTAINER_PERMISSIONS); TangibleObject bank = (TangibleObject) core.objectService.createObject("object/tangible/bank/shared_character_bank.iff", object.getPlanet()); bank.setContainerPermissions(CreatureContainerPermissions.CREATURE_CONTAINER_PERMISSIONS); TangibleObject missionBag = (TangibleObject) core.objectService.createObject("object/tangible/mission_bag/shared_mission_bag.iff", object.getPlanet()); missionBag.setContainerPermissions(CreatureContainerPermissions.CREATURE_CONTAINER_PERMISSIONS); object._add(inventory); object._add(appInventory); object._add(datapad); object._add(bank); object._add(missionBag); TangibleObject backpack = (TangibleObject) core.objectService.createObject("object/tangible/wearables/backpack/shared_backpack_galactic_marine.iff", object.getPlanet()); inventory._add(backpack); //object.addObjectToEquipList(datapad); //object.addObjectToEquipList(inventory); WeaponObject weapon = (WeaponObject) core.objectService.createObject("object/weapon/ranged/rifle/shared_rifle_a280.iff", object.getPlanet()); WeaponObject defaultWeapon = (WeaponObject) core.objectService.createObject("object/weapon/creature/shared_creature_default_weapon.iff", object.getPlanet()); //object.addObjectToEquipList(defaultWeapon); object._add(defaultWeapon); //object.addObjectToEquipList(weapon); object._add(weapon); object.setWeaponId(weapon.getObjectID()); core.scriptService.callScript("scripts/", "starterclothing", "CreateStarterClothing", core, object, clientCreateCharacter.getStarterProfession(), clientCreateCharacter.getRaceTemplate()); core.scriptService.callScript("scripts/", "CreateStartingCharacter", "demo", core, object); core.getCreatureODB().put(object, Long.class, CreatureObject.class, object.getTransaction()); // might not need to commit transaction but better safe than sorry object.getTransaction().commitSync(); PreparedStatement ps = databaseConnection.preparedStatement("INSERT INTO characters (id, \"firstName\", \"lastName\", \"accountId\", \"galaxyId\", \"statusId\", appearance, gmflag) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); ps.setLong(1, object.getObjectID()); ps.setString(2, clientCreateCharacter.getFirstName()); ps.setString(3, clientCreateCharacter.getLastName()); ps.setInt(4, (int) client.getAccountId()); // accountID ps.setInt(5, galaxyId); ps.setInt(6, 1); ps.setInt(7, CRC.StringtoCRC(raceTemplate)); ps.setBoolean(8, false); ps.executeUpdate(); ps.close(); CreateCharacterSuccess success = new CreateCharacterSuccess(object.getObjectID()); session.write(new HeartBeatMessage().serialize()); session.write(core.loginService.getLoginCluster().serialize()); session.write(core.loginService.getLoginClusterStatus().serialize()); session.write(success.serialize()); session.write((new ClientMfdStatusUpdateMessage((float) 2, "/GroundHUD.MFDStatus.vsp.role.targetLevel")).serialize()); } }); } private boolean checkForDuplicateName(String firstName, long accountId) throws SQLException { firstName = firstName.replace("'", "''"); firstName = firstName.toLowerCase(); PreparedStatement ps = databaseConnection.preparedStatement("SELECT id FROM characters WHERE LOWER(\"firstName\")=?"); ps.setString(1, firstName); ResultSet resultSet = ps.executeQuery(); boolean isDuplicate = resultSet.next(); resultSet.getStatement().close(); //FIXME: this is a bit lazy... but it's only temporary :p PreparedStatement ps2 = databaseConnection.preparedStatement("SELECT id FROM temp_reserved_char_names WHERE \"accountId\"!=? AND LOWER(\"firstName\")=?"); ps2.setLong(1, accountId); ps2.setString(2, firstName); ResultSet resultSet2 = ps2.executeQuery(); boolean isReserved = resultSet.next(); resultSet2.getStatement().close(); return isDuplicate && isReserved; } private String getfirstName(String Name, String RaceTemplate) { String[] splitName; if (RaceTemplate != "object/creature/player/wookiee_male.iff" | RaceTemplate != "object/creature/player/wookiee_female.iff") { // wookies don't have lastNames if (Name.contains(" ")) { splitName = Name.split(" ", 2); return splitName[0]; } else { return Name; } } else { return Name; } } private String getlastName(String Name, String RaceTemplate) { String[] splitName; if (RaceTemplate != "object/creature/player/wookiee_male.iff" && RaceTemplate != "object/creature/player/wookiee_female.iff") { // wookies don't have lastNames if (Name.contains(" ")) { splitName = Name.split(" ", 2); return splitName[1]; } else { return ""; } } else { return ""; } } private String checkForReservedName(String firstName, String lastName) throws SQLException { firstName = firstName.toLowerCase(); lastName = lastName.toLowerCase(); firstName.replaceAll("'", ""); firstName.replaceAll("-", ""); lastName.replaceAll("'", ""); lastName.replaceAll("-", ""); String combinedName = firstName + " " + lastName; combinedName.replaceAll("\\s*$", ""); String query = "SELECT \"type\" FROM \"reservednames\" WHERE (\"match_firstname\" = TRUE AND ? ~ \"pattern\") OR (\"match_both_names\" = TRUE and ? ~ \"pattern\") "; if (lastName != "") { query = query + " OR (\"match_lastname\" = TRUE and ? ~ \"pattern\") "; } PreparedStatement ps = databaseConnection.preparedStatement(query); ps.setString(1, firstName); ps.setString(2, combinedName); if (lastName != "") { ps.setString(3, lastName); } ResultSet resultSet = ps.executeQuery(); if (resultSet.next()) { return resultSet.getString("type"); } return null; } @Override public void shutdown() { // TODO Auto-generated method stub } }