diff --git a/.gitignore b/.gitignore index 24eef345..de39aaf3 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,8 @@ odb/cities/je.info.* # PDT-specific .buildpath +# Log files +debuglogs/ ################# ## Visual Studio diff --git a/src/main/NGECore.java b/src/main/NGECore.java index c7adeb9c..b0cb66d6 100644 --- a/src/main/NGECore.java +++ b/src/main/NGECore.java @@ -111,6 +111,7 @@ import services.spawn.SpawnService; import services.sui.SUIService; import services.trade.TradeService; import services.travel.TravelService; +import tools.DevLogQueuer; import engine.clientdata.ClientFileManager; import engine.clientdata.visitors.CrcStringTableVisitor; import engine.clientdata.visitors.DatatableVisitor; @@ -535,6 +536,9 @@ public class NGECore { browserService = new BrowserService(this); + DevLogQueuer devLogQueuer = new DevLogQueuer(); + + didServerCrash = false; System.out.println("Started Server."); cleanupCreatureODB(); diff --git a/src/resources/objects/SWGList.java b/src/resources/objects/SWGList.java index 8415a535..f0ca3665 100644 --- a/src/resources/objects/SWGList.java +++ b/src/resources/objects/SWGList.java @@ -341,7 +341,7 @@ public class SWGList implements List, Serializable { this.list = list; updateCounter += list.size(); - StringUtilities.printBytes(newListData); + //StringUtilities.printBytes(newListData); queue(newListData); return true; diff --git a/src/resources/objects/tangible/TangibleObject.java b/src/resources/objects/tangible/TangibleObject.java index 407ea38e..2a62ead1 100644 --- a/src/resources/objects/tangible/TangibleObject.java +++ b/src/resources/objects/tangible/TangibleObject.java @@ -48,6 +48,7 @@ import resources.objects.ObjectMessageBuilder; import resources.objects.SWGList; import resources.objects.SWGSet; import resources.objects.creature.CreatureObject; +import tools.DevLog; import engine.clientdata.ClientFileManager; import engine.clientdata.visitors.IDManagerVisitor; import engine.clients.Client; @@ -328,8 +329,13 @@ public class TangibleObject extends SWGObject implements Serializable { } public void addDefender(TangibleObject defender) { + if (((CreatureObject)this).getOwnerId()>0){ + if (((CreatureObject)this).getOwnerId()==defender.getObjectID()){ + return; // fix for now until determined where the tamer is added from + } + } defendersList.add(defender); - + if (!isInCombat()) { setInCombat(true); } diff --git a/src/services/CharacterService.java b/src/services/CharacterService.java index 948a49b8..3fa7a8df 100644 --- a/src/services/CharacterService.java +++ b/src/services/CharacterService.java @@ -268,6 +268,7 @@ public class CharacterService implements INetworkDispatch { object.setPosition(SpawnPoint.getRandomPosition(new Point3D(3528, 0, -4804), (float) 0.5, 3, core.terrainService.getPlanetByName("tatooine").getID())); object.setCashCredits(100); object.setBankCredits(1000); + object.setIncapTimer(60); object.setOptionsBitmask(Options.ATTACKABLE); //object.setPosition(new Point3D(0, 0, 0)); object.setOrientation(new Quaternion(1, 0, 0, 0)); diff --git a/src/services/DevService.java b/src/services/DevService.java index 91b23e8f..259b0e75 100644 --- a/src/services/DevService.java +++ b/src/services/DevService.java @@ -35,6 +35,7 @@ import resources.common.Console; import resources.common.FileUtilities; import resources.common.Opcodes; import resources.common.SpawnPoint; +import resources.datatables.Posture; import resources.datatables.WeaponType; import resources.harvest.SurveyTool; import resources.objects.building.BuildingObject; @@ -1062,6 +1063,7 @@ public class DevService implements INetworkDispatch { return; case 110: + player.setPosture(Posture.Incapacitated); SurveyTool mineralSurveyTool = (SurveyTool) core.objectService.createObject("object/tangible/survey_tool/shared_survey_tool_mineral.iff", planet); mineralSurveyTool.setCustomName("Mineral Survey Device"); inventory.add(mineralSurveyTool); diff --git a/src/services/LootService.java b/src/services/LootService.java index 4f9b872e..45cebc89 100644 --- a/src/services/LootService.java +++ b/src/services/LootService.java @@ -62,6 +62,7 @@ import services.sui.SUIService.MessageBoxType; import services.sui.SUIWindow; import services.sui.SUIWindow.SUICallback; import services.sui.SUIWindow.Trigger; +import tools.DevLog; import main.NGECore; import engine.resources.container.CreatureContainerPermissions; import engine.resources.container.Traverser; @@ -104,8 +105,8 @@ public class LootService implements INetworkDispatch { // security check if (hasAccess(requester,lootedObject) && ! lootedObject.isLooted()){ LootRollSession lootRollSession = (LootRollSession )lootedObject.getAttachment("LootSession"); - if (lootRollSession.getDroppedItems().size()==0){ - System.err.println("lootRollSession.getDroppedItems().size()==0"); + if (lootRollSession.getDroppedItems().size()==0){ + DevLog.debugout("Charon", "Loot Service", "handleLootRequest lootRollSession.getDroppedItems().size()==0"); return; } SWGObject lootedObjectInventory = lootedObject.getSlottedObject("inventory"); @@ -117,7 +118,7 @@ public class LootService implements INetworkDispatch { private boolean hasAccess(CreatureObject requester, TangibleObject lootedObject){ LootRollSession lootRollSession = (LootRollSession )lootedObject.getAttachment("LootSession"); if (lootRollSession==null) - System.err.println("LootSession null: " + lootRollSession); + DevLog.debugout("Charon", "Loot Service", "hasAccess LootSession null: " + lootRollSession); if (lootRollSession!=null){ if (lootRollSession.getRequester()==requester){ return true; @@ -169,7 +170,6 @@ public class LootService implements INetworkDispatch { if (projectionCoefficientMatrixModulo!=0) lootGroupRoll=groupChance+1; if (lootGroupRoll <= groupChance){ - //System.out.println("this lootGroup will drop something"); handleLootGroup(lootGroup,lootRollSession); //this lootGroup will drop something e.g. {kraytpearl_range,krayt_tissue_rare} } } @@ -502,11 +502,11 @@ public class LootService implements INetworkDispatch { double[] lootPoolChances = lootGroup.getLootPoolChances(); String[] lootPoolNames = lootGroup.getLootPoolNames(); if (lootPoolChances==null || lootPoolNames==null){ - System.err.println("Lootpools are null!"); + DevLog.debugout("Charon", "Loot Service", "handleLootGroup Lootpools are null!"); return; } if (lootPoolChances.length==0 || lootPoolNames.length==0){ - System.err.println("No Lootpools in Lootgroup!"); + DevLog.debugout("Charon", "Loot Service", "handleLootGroup No Lootpools in Lootgroup!"); return; } @@ -529,7 +529,7 @@ public class LootService implements INetworkDispatch { } } if (!test) - System.err.println("SOMETHING WENT WRONG!"); + DevLog.debugout("Charon", "Loot Service", "handleLootGroup SOMETHING WENT WRONG!"); } private void handleLootPool(String poolName,LootRollSession lootRollSession){ @@ -991,11 +991,11 @@ public class LootService implements INetworkDispatch { double[] lootPoolChances = lootGroup.getLootPoolChances(); String[] lootPoolNames = lootGroup.getLootPoolNames(); if (lootPoolChances==null || lootPoolNames==null){ - System.err.println("Lootpools are null!"); + DevLog.debugout("Charon", "Loot Service", "fillChest2 Lootpools are null!"); return; } if (lootPoolChances.length==0 || lootPoolNames.length==0){ - System.err.println("No Lootpools in Lootgroup!"); + DevLog.debugout("Charon", "Loot Service", "fillChest2 No Lootpools in Lootgroup!"); return; } @@ -1018,7 +1018,7 @@ public class LootService implements INetworkDispatch { } } if (!test) - System.err.println("SOMETHING WENT WRONG!"); + DevLog.debugout("Charon", "Loot Service", "fillChest2 SOMETHING WENT WRONG!"); } private void fillChest3(String poolName, CreatureObject owner, TangibleObject chest){ @@ -1289,7 +1289,7 @@ public class LootService implements INetworkDispatch { // Example color crystal if (itemName.contains("colorcrystal")) { - System.out.println("colorcrystal"); + DevLog.debugout("Charon", "Loot Service", "setCustomization colorcrystal"); if (customizationAttributes==null){ int crystalColor = new Random().nextInt(11); @@ -1316,7 +1316,7 @@ public class LootService implements INetworkDispatch { // Example power crystal if (itemName.contains("powercrystal")) { - System.out.println("powercrystal"); + DevLog.debugout("Charon", "Loot Service", "setCustomization powercrystal"); droppedItem.setCustomizationVariable("/private/index_color_1", (byte) 0x21); // 0x1F } @@ -1324,7 +1324,7 @@ public class LootService implements INetworkDispatch { if (lootDescriptor==null) lootDescriptor=""; if (lootDescriptor.contains("rarecolorcrystal")) { - System.out.println("rarecolorcrystal"); + DevLog.debugout("Charon", "Loot Service", "setCustomization rarecrystal"); int saberColor = (Integer)droppedItem.getAttachment("customColor1"); if (saberColor==31) // Cunning of Tyranus, which is not in @jedi_spam @@ -2316,8 +2316,8 @@ public class LootService implements INetworkDispatch { lootRollSession.setLootedObjectDifficulty(0); if (containerObject==null) - System.out.println("containerObject is NULL " ); - + DevLog.debugout("Charon", "Loot Service", "handleContainer containerObject is NULL"); + handleContainerDrops(containerObject, lootRollSession); } @@ -2343,11 +2343,11 @@ public class LootService implements INetworkDispatch { double[] lootPoolChances = lootGroup.getLootPoolChances(); String[] lootPoolNames = lootGroup.getLootPoolNames(); if (lootPoolChances==null || lootPoolNames==null){ - System.err.println("Lootpools are null!"); + DevLog.debugout("Charon", "Loot Service", "handleContainerLootGroup Lootpools are null!"); return; } if (lootPoolChances.length==0 || lootPoolNames.length==0){ - System.err.println("No Lootpools in Lootgroup!"); + DevLog.debugout("Charon", "Loot Service", "handleContainerLootGroup No Lootpools in Lootgroup!"); return; } @@ -2370,11 +2370,11 @@ public class LootService implements INetworkDispatch { } } if (!test) - System.err.println("SOMETHING WENT WRONG!"); + DevLog.debugout("Charon", "Loot Service", "handleContainerLootGroup SOMETHING WENT WRONG!"); } private void handleContainerLootPool(String poolName,LootRollSession lootRollSession , TangibleObject containerObject){ - System.err.println("poolName.toLowerCase() " + poolName.toLowerCase()); + DevLog.debugout("Charon", "Loot Service", "handleContainerLootPool poolName.toLowerCase() " + poolName.toLowerCase()); // Fetch the loot pool data from the poolName.py script String path = "scripts/loot/lootPools/"+poolName.toLowerCase(); Vector itemNames = (Vector)core.scriptService.fetchStringVector(path,"itemNames"); @@ -2502,7 +2502,7 @@ public class LootService implements INetworkDispatch { junkType = (byte)core.scriptService.fetchInteger(itemPath,"junkType"); - System.out.println("itemTemplate " + itemTemplate); + DevLog.debugout("Charon", "Loot Service", "handleContainerLootPoolItems itemTemplate " + itemTemplate); TangibleObject droppedItem = createDroppedItem(itemTemplate,lootRollSession.getSessionPlanet()); @@ -2567,7 +2567,7 @@ public class LootService implements INetworkDispatch { //SWGObject containerObjectInventory = containerObject.getSlottedObject("inventory"); //placable_loot_crate_trashpile containerObject.add(droppedItem); //containerObjectInventory.add(droppedItem); - System.out.println("Added to container " + containerObject.getTemplate()); + DevLog.debugout("Charon", "Loot Service", "handleContainerLootPoolItems Added to container " + containerObject.getTemplate()); } @@ -2947,7 +2947,6 @@ public class LootService implements INetworkDispatch { sword1.setOptions(Options.USABLE, true); sword1.setWeaponType(WeaponType.ONEHANDEDMELEE); playerInventory.add(sword1); - System.err.println("MIX " + sword1.getObjectID()); String powerUpLabel = "item_reverse_engineering_powerup_weapon_02_01"; diff --git a/src/services/ai/AIActor.java b/src/services/ai/AIActor.java index b1d391bd..5d6c7673 100644 --- a/src/services/ai/AIActor.java +++ b/src/services/ai/AIActor.java @@ -49,6 +49,7 @@ import services.ai.states.PatrolState; import services.ai.states.RetreatState; import services.combat.CombatEvents.DamageTaken; import services.spawn.MobileTemplate; +import tools.DevLog; import java.util.Random; @@ -107,10 +108,13 @@ public class AIActor { if(creature == null || creature.getObservers().isEmpty() || creature.isInCombat() || isStalking) return; if (creature.getAttachment("tamed")!=null){ - boolean tamed = (boolean)creature.getAttachment("tamed"); - if (!tamed) - return; + DevLog.debugout("Charon", "Pet AI", "aggroCheckTask tamed==1"); + return; } + + if (creature.getCustomName().contains("baby")) + DevLog.debugout("Charon", "Pet AI", "baby aggroCheckTask should not be here " +creature.getAttachment("tamed")); + creature.getObservers().stream().map(Client::getParent).filter(obj -> obj.inRange(creature.getWorldPosition(), 15)).forEach((obj) -> { if(new Random().nextFloat() <= 0.5 || creature.isInCombat() || isStalking) { /*if(mobileTemplate.isStalker()) { @@ -118,6 +122,9 @@ public class AIActor { setCurrentState(new StalkState()); } else */ + if (creature.getAttachment("IsBeingTamed")!=null) + return; + if (obj instanceof CreatureObject){ CreatureObject addedObject = (CreatureObject) obj; if (addedObject.getCalledPet()!=null){ @@ -292,10 +299,10 @@ public class AIActor { public void faceObject(SWGObject object) { // Null checks due to a null error at: float direction = - if (object == null) System.out.println("object is null"); - if (creature == null) System.out.println("creature is null"); - if (object.getWorldPosition() == null) System.out.println("object's position is null"); - if (creature.getWorldPosition() == null) System.out.println("creature's position is null"); + if (object == null) DevLog.debugout("Charon", "AI Actor", "faceObject object is NULL"); + if (creature == null) DevLog.debugout("Charon", "AI Actor", "faceObject creature is NULL"); + if (object.getWorldPosition() == null) DevLog.debugout("Charon", "AI Actor", "faceObject object's position is NULL"); + if (creature.getWorldPosition() == null) DevLog.debugout("Charon", "AI Actor", "faceObject creature's position is NULL"); float direction = (float) Math.atan2(object.getWorldPosition().x - creature.getWorldPosition().x, object.getWorldPosition().z - creature.getWorldPosition().z); if(direction < 0) @@ -394,6 +401,10 @@ public class AIActor { public void destroyActor(){ creature.getEventBus().unsubscribe(this); // Make sure to kill all AI helper threads + if (aggroCheckTask!=null) + aggroCheckTask.cancel(true); + if (regenTask!=null) + regenTask.cancel(true); if (movementFuture!=null){ movementFuture.cancel(true); movementFuture = null; @@ -408,6 +419,13 @@ public class AIActor { } } + public void cancelAggro(){ + try { + aggroCheckTask.cancel(true); + } catch(Exception e) { + } + } + public ScheduledFuture getRegenTask() { return regenTask; } diff --git a/src/services/ai/LairActor.java b/src/services/ai/LairActor.java index 1b7b8970..87e5940b 100644 --- a/src/services/ai/LairActor.java +++ b/src/services/ai/LairActor.java @@ -31,6 +31,7 @@ import resources.common.SpawnPoint; import resources.objects.creature.CreatureObject; import resources.objects.tangible.TangibleObject; import services.combat.CombatEvents.DamageTaken; +import tools.DevLog; public class LairActor { @@ -138,14 +139,14 @@ public class LairActor { creatureTemplate = creatureTemplates.get(new Random().nextInt(creatureTemplates.size())); float babyChance = new Random().nextFloat(); CreatureObject creature = null; - if (babyChance>0.1){ + if (babyChance>0.9){ creature = NGECore.getInstance().spawnService.spawnCreature(creatureTemplate, lairObject.getPlanet().getName(), 0, position.x, position.y, position.z, level); } else { creature = NGECore.getInstance().spawnService.spawnCreatureBaby(creatureTemplate, lairObject.getPlanet().getName(), 0, position.x, position.y, position.z, level); } if(creature == null || !creature.isInQuadtree()) { - System.out.println("NULL Creature"); + DevLog.debugout("Charon", "Lair AI", "LairActor spawnNewCreatures() NULL creature!"); continue; } creatures.add((AIActor) creature.getAttachment("AI")); diff --git a/src/services/ai/states/AttackState.java b/src/services/ai/states/AttackState.java index e487a9f9..90233e6b 100644 --- a/src/services/ai/states/AttackState.java +++ b/src/services/ai/states/AttackState.java @@ -25,15 +25,16 @@ import java.util.Random; import java.util.Vector; import main.NGECore; - import resources.objects.creature.CreatureObject; import resources.objects.weapon.WeaponObject; import services.ai.AIActor; +import tools.DevLog; public class AttackState extends AIState { @Override public byte onEnter(AIActor actor) { + DevLog.debugout("Charon", "AI Attack State", "onEnter"); CreatureObject creature = actor.getCreature(); if(creature.getPosture() == 14) return StateResult.DEAD; @@ -47,9 +48,11 @@ public class AttackState extends AIState { @Override public byte onExit(AIActor actor) { // TODO Auto-generated method stub + actor.getCreature().setLookAtTarget(0); actor.getCreature().setIntendedTarget(0); - + actor.setFollowObject(null); + DevLog.debugout("Charon", "AI Attack State", "onExit"); return StateResult.FINISHED; } @@ -129,20 +132,25 @@ public class AttackState extends AIState { target = actor.getFollowObject(); } if(target == null) { - System.out.println("null target"); + DevLog.debugout("Charon", "AI Attack State", "null target"); actor.scheduleRecovery(); return StateResult.UNFINISHED; } if(target.getPosture() == 13 || target.getPosture() == 14 || target.isInStealth()) { - actor.removeDefender(target); - actor.setFollowObject(actor.getHighestDamageDealer()); + + actor.setFollowObject(actor.getHighestDamageDealer()); target = actor.getFollowObject(); if(target == null) { creature.setLookAtTarget(0); creature.setIntendedTarget(0); - return StateResult.FINISHED; + } + actor.setFollowObject(null); + actor.removeDefender(target); + + actor.setCurrentState(new RetreatState()); + return StateResult.FINISHED; } if(target.getWorldPosition().getDistance(creature.getWorldPosition()) > 128 || target.getPosture() == 13 || target.getPosture() == 14) { actor.removeDefender(target); @@ -157,6 +165,10 @@ public class AttackState extends AIState { Vector attacks = actor.getMobileTemplate().getAttacks(); + // Pet +// if (creature.getOwnerId()>0) +// attacks = creature.getSpecialAttacks(); + creature.setLookAtTarget(target.getObjectId()); creature.setIntendedTarget(target.getObjectId()); diff --git a/src/services/ai/states/DeathState.java b/src/services/ai/states/DeathState.java index 6ef981bd..3ecdf65a 100644 --- a/src/services/ai/states/DeathState.java +++ b/src/services/ai/states/DeathState.java @@ -38,7 +38,10 @@ public class DeathState extends AIState { CreatureObject killer = (CreatureObject)actor.getCreature().getKiller(); if (killer==null) killer = actor.getHighestDamageDealer(); - NGECore.getInstance().lootService.DropLoot(killer,(TangibleObject)(actor.getCreature())); + NGECore.getInstance().lootService.DropLoot(killer,(TangibleObject)(actor.getCreature())); + + actor.destroyActor(); // to prevent standing up right after death + return 0; } diff --git a/src/services/ai/states/RetreatState.java b/src/services/ai/states/RetreatState.java index 00aa451e..b94684fe 100644 --- a/src/services/ai/states/RetreatState.java +++ b/src/services/ai/states/RetreatState.java @@ -23,6 +23,7 @@ package services.ai.states; import main.NGECore; import engine.resources.scene.Point3D; +import resources.objects.creature.CreatureObject; import services.ai.AIActor; public class RetreatState extends AIState { @@ -35,7 +36,14 @@ public class RetreatState extends AIState { } }*/ NGECore.getInstance().aiService.logAI("RetreatState ENTER "); - actor.setFollowObject(null); + + if (actor.getCreature().getOwnerId()>0){ + CreatureObject owner = (CreatureObject) NGECore.getInstance().objectService.getObject(actor.getCreature().getOwnerId()); + actor.setFollowObject(owner); + actor.setCurrentState(new FollowState()); + return StateResult.FINISHED; + } else + actor.setFollowObject(null); actor.scheduleMovement(); return StateResult.UNFINISHED; } diff --git a/src/services/combat/CombatService.java b/src/services/combat/CombatService.java index 83c6c742..8ab9adcb 100644 --- a/src/services/combat/CombatService.java +++ b/src/services/combat/CombatService.java @@ -66,6 +66,7 @@ import services.sui.SUIService.MessageBoxType; import services.sui.SUIWindow; import services.sui.SUIWindow.SUICallback; import services.sui.SUIWindow.Trigger; +import tools.DevLog; import main.NGECore; import engine.resources.common.CRC; import engine.resources.objects.SWGObject; @@ -312,18 +313,21 @@ public class CombatService implements INetworkDispatch { } private void applyDamage(CreatureObject attacker, TangibleObject target, int damage) { + target.setConditionDamage(target.getConditionDamage() + damage); if (target.getOption(Options.MOUNT)) { core.mountService.damage((CreatureObject) target); } - + if (target instanceof CreatureObject){ CreatureObject targetCreature = (CreatureObject)target; if (targetCreature.getCalledPet()!=null) { AIActor petActor = (AIActor) targetCreature.getCalledPet().getAttachment("AI"); - if (petActor!=null) + if (petActor!=null){ petActor.addDefender(attacker); + DevLog.debugout("Charon", "Pet AI", "applyDamage addDefender"); + } } } @@ -331,7 +335,7 @@ public class CombatService implements INetworkDispatch { event.attacker = attacker; event.damage = damage; target.getEventBus().publish(event); - + System.out.println("APPLY DAMAGE2"); attacker.setTefTime(300000); } @@ -620,7 +624,7 @@ public class CombatService implements INetworkDispatch { } private float calculateDamage(CreatureObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command) { - + if(target.getBuffByName("me_stasis_self_1") != null || target.getBuffByName("me_stasis_1") != null) return 0; @@ -669,14 +673,18 @@ public class CombatService implements INetworkDispatch { if(target.getSkillMod("combat_divide_damage_dealt") != null) { rawDamage *= (1 - (target.getSkillMod("combat_divide_damage_dealt").getBase() / 100)); } - + + + //Hook in here to agitate called pet + + return rawDamage; } private float calculateDamage(CreatureObject attacker, TangibleObject target, WeaponObject weapon, CombatCommand command) { - + float rawDamage = command.getAddedDamage(); if(command.getPercentFromWeapon() > 0 && weapon != attacker.getSlottedObject("default_weapon")) { @@ -833,7 +841,14 @@ public class CombatService implements INetworkDispatch { if(target.getHealth() - damage <= 0 && target.getSlottedObject("ghost") != null) { if(target.hasBuff("incapWeaken")) { - deathblowPlayer(attacker, target); + if (!attacker.isPlayer()){ + AIActor aiActor = (AIActor)attacker.getAttachment("AI"); + if (aiActor.getMobileTemplate().isDeathblow()) + deathblowPlayer(attacker, target); + } else + { + deathblowPlayer(attacker, target); + } return; } @@ -845,8 +860,10 @@ public class CombatService implements INetworkDispatch { target.setHealth(1); target.setPosture(Posture.Incapacitated); target.setTurnRadius(0); - target.setSpeedMultiplierBase(0); + target.setSpeedMultiplierBase(0); + } + ScheduledFuture incapTask = scheduler.schedule(() -> { synchronized(target.getMutex()) { @@ -882,10 +899,26 @@ public class CombatService implements INetworkDispatch { synchronized(target.getMutex()) { target.setHealth(target.getHealth() - damage); } - DamageTaken event = events.new DamageTaken(); - event.attacker = attacker; - event.damage = damage; - target.getEventBus().publish(event); + + + if (target instanceof CreatureObject){ + CreatureObject targetCreature = (CreatureObject)target; + if (targetCreature.getCalledPet()!=null) { + AIActor petActor = (AIActor) targetCreature.getCalledPet().getAttachment("AI"); + if (petActor!=null){ + petActor.addDefender(attacker); + DevLog.debugout("Charon", "Pet AI", "applyDamage addDefender"); + } + } + } + + + if (target.getPosture()!=13){ + DamageTaken event = events.new DamageTaken(); + event.attacker = attacker; + event.damage = damage; + target.getEventBus().publish(event); + } } public void doDrainHeal(CreatureObject receiver, int drainAmount) { diff --git a/src/services/pet/MountService.java b/src/services/pet/MountService.java index 21be4f20..203fa2db 100644 --- a/src/services/pet/MountService.java +++ b/src/services/pet/MountService.java @@ -37,6 +37,7 @@ import resources.objects.building.BuildingObject; import resources.objects.creature.CreatureObject; import resources.objects.player.PlayerObject; import services.ai.AIActor; +import tools.DevLog; import engine.clientdata.ClientFileManager; import engine.clientdata.visitors.DatatableVisitor; import engine.resources.container.Traverser; @@ -253,7 +254,7 @@ public class MountService implements INetworkDispatch { mount.setPosition(actor.getPosition().clone()); mount.setOrientation(actor.getOrientation().clone()); mount.setPlanet(actor.getPlanet()); - System.out.println("PROCESS MOUNT " + mount.getTemplate()); + DevLog.debugout("Charon", "Mount Service", "PROCESS MOUNT " + mount.getTemplate()); core.simulationService.add(mount, actor.getWorldPosition().x, actor.getWorldPosition().z, false); } } diff --git a/src/services/pet/PetService.java b/src/services/pet/PetService.java index 2b4ff898..27608cb0 100644 --- a/src/services/pet/PetService.java +++ b/src/services/pet/PetService.java @@ -44,6 +44,7 @@ import resources.objects.creature.CreatureObject; import resources.objects.player.PlayerObject; import services.ai.AIActor; import services.ai.states.FollowState; +import tools.DevLog; import engine.clientdata.ClientFileManager; import engine.clientdata.visitors.DatatableVisitor; import engine.resources.container.Traverser; @@ -57,6 +58,8 @@ public class PetService implements INetworkDispatch { public PetService(NGECore core) { this.core = core; + DevLog.enableMe(); + DevLog.enableFileLogging(); } @@ -66,7 +69,9 @@ public class PetService implements INetworkDispatch { } public void call(CreatureObject actor, CreatureObject pet, SWGObject pcd) { - System.out.println("Call called !"); + + DevLog.debugout("Charon", "Pet AI", "Call called !"); + if (actor == null) { return; } @@ -78,7 +83,7 @@ public class PetService implements INetworkDispatch { storeAll(actor); // Store other pets if (pcd == null) { - System.out.println("(pcd == null)"); + DevLog.debugout("Charon", "Pet AI", "call method (pcd == null)"); actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_call"), DisplayType.Broadcast); return; } @@ -125,7 +130,7 @@ public class PetService implements INetworkDispatch { pet = (CreatureObject) NGECore.getInstance().staticService.spawnObject(petTemplate, actor.getPlanet().getName(), 0L, actor.getWorldPosition().x, actor.getWorldPosition().y, actor.getWorldPosition().z, actor.getOrientation().y, actor.getOrientation().w); if (pet == null) { - System.out.println("mount == null)"); + DevLog.debugout("Charon", "Pet AI", "call method mount == null"); // Somehow the vehicle object has got lost actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_call"), DisplayType.Broadcast); return; @@ -703,7 +708,7 @@ public class PetService implements INetworkDispatch { */ public void store(CreatureObject storer, CreatureObject mount) { if (mount == null) { - System.err.println("PetService:store(): mount is null; this should never be the case."); + DevLog.debugout("Charon", "Pet AI", "PetService:store(): mount is null; this should never be the case."); return; } @@ -753,9 +758,9 @@ public class PetService implements INetworkDispatch { AIActor aiActor = (AIActor) mount.getAttachment("AI"); aiActor.destroyActor(); player.setPet(0L); + owner.setCalledPet(null); core.objectService.destroyObject((Long) pcd.getAttachment("companion_RefId")); pcd.setAttachment("companion_RefId", null); - System.out.println("STORE called !"); } } } @@ -785,11 +790,12 @@ public class PetService implements INetworkDispatch { AIActor aiActor = (AIActor) pet.getAttachment("AI"); aiActor.destroyActor(); - + + actor.setCalledPet(null); + core.objectService.destroyObject(petId); pcd.setAttachment("companion_RefId",null); player.setPet(0L); - System.out.println("STOREALL called !"); } } } @@ -906,18 +912,24 @@ public class PetService implements INetworkDispatch { } public void tame(CreatureObject actor, CreatureObject target) { - System.out.println("TAME!"); + DevLog.debugout("Charon", "Pet AI", "Tame called."); + + if (actor.getCalledPet()!=null){ + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:too_many"), DisplayType.Broadcast); + return; + } Thread tameThread = new Thread() { public void run() { try { boolean attacking = false; AIActor aiActor = (AIActor) target.getAttachment("AI"); - actor.sendSystemMessage("Don't be scared.", DisplayType.Broadcast); + target.setAttachment("IsBeingTamed",true); + //actor.sendSystemMessage("Don't be scared.", DisplayType.Broadcast); OutOfBand oob = OutOfBand.ProsePackage("Don't be scared."); NGECore.getInstance().chatService.spatialChat(actor, target, "Don't be scared.", (short)0x0, (short)0x0, 1, oob); Thread.sleep(4000); - actor.sendSystemMessage("Steady.", DisplayType.Broadcast); + //actor.sendSystemMessage("Steady.", DisplayType.Broadcast); OutOfBand oob2 = OutOfBand.ProsePackage("Steady."); NGECore.getInstance().chatService.spatialChat(actor, target, "Steady.", (short)0x0, (short)0x0, 1, oob2); Thread.sleep(5000); @@ -930,26 +942,34 @@ public class PetService implements INetworkDispatch { target.setAttachment("radial_filename", "npc/untamable"); aiActor.setFollowObject(actor); aiActor.addDefender(actor); + target.setAttachment("IsBeingTamed",null); + if (target.getPosture()==14) + return; } - actor.sendSystemMessage("Don't bite me.", DisplayType.Broadcast); + //actor.sendSystemMessage("Don't bite me.", DisplayType.Broadcast); OutOfBand oob3 = OutOfBand.ProsePackage("Don't bite me."); NGECore.getInstance().chatService.spatialChat(actor, target, "Don't bite me.", (short)0x0, (short)0x0, 1, oob3); Thread.sleep(3000); // Chance to attack attackChance = new Random().nextFloat(); if (attackChance<0.45 || attacking){ + if (target.getPosture()==14) + return; attacking = true; target.setAttachment("tamed",null); aiActor.setFollowObject(actor); aiActor.addDefender(actor); actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:too_hard"), DisplayType.Broadcast); target.setAttachment("radial_filename", "npc/untamable"); + target.setAttachment("IsBeingTamed",null); return; } // Result of taming float tamingResult = new Random().nextFloat(); - System.out.println("tamingResult " + tamingResult); + DevLog.debugout("Charon", "Pet AI", "tamingResult " + tamingResult); if (tamingResult<0.5){ + if (target.getPosture()==14) + return; actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:device_added"), DisplayType.Broadcast); String pcdTemplate = "object/intangible/pet/shared_pet_control.iff"; SWGObject pcd = core.objectService.createObject(pcdTemplate, actor.getPlanet()); @@ -965,21 +985,21 @@ public class PetService implements INetworkDispatch { if (target.getOption(Options.AGGRESSIVE)) target.removeOption(Options.AGGRESSIVE); - if (target.getOption(Options.ATTACKABLE)) - target.removeOption(Options.ATTACKABLE); - + + target.setAttachment("tamed", 1); + actor.setCalledPet(target); target.setFaction(actor.getFaction()); target.setFactionStatus(actor.getFactionStatus()); target.setOwnerId(actor.getObjectID()); target.setAttachment("radial_filename", "npc/pet_radial"); - target.setAttachment("tamed", true); - - - aiActor.removeDefender(aiActor.getFollowObject()); // Make sure tamer is no enemy anymore + target.setAttachment("radial_filename", "npc/pet_radial"); + + aiActor.cancelAggro(); + aiActor.removeDefender(actor); // Make sure tamer is no enemy anymore aiActor.setFollowObject(actor); aiActor.setCurrentState(new FollowState()); - + target.setAttachment("IsBeingTamed",null); } else { actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:too_hard"), DisplayType.Broadcast); } diff --git a/src/services/spawn/DynamicSpawnArea.java b/src/services/spawn/DynamicSpawnArea.java index 81635232..6ff91997 100644 --- a/src/services/spawn/DynamicSpawnArea.java +++ b/src/services/spawn/DynamicSpawnArea.java @@ -35,6 +35,7 @@ import resources.common.collidables.AbstractCollidable.ExitEvent; import resources.objects.creature.CreatureObject; import services.TerrainService; import services.SimulationService.MoveEvent; +import tools.DevLog; import engine.resources.objects.SWGObject; import engine.resources.scene.Planet; import engine.resources.scene.Point3D; @@ -278,7 +279,7 @@ public class DynamicSpawnArea extends SpawnArea { String template = spawnGroup.getMobiles().get(spawnIndex); MobileTemplate mobileTemplate = NGECore.getInstance().spawnService.getMobileTemplate(template); if (mobileTemplate==null){ - System.out.println("mobileTemplate==null for template " + template); + DevLog.debugout("Charon", "Dynamic Spawn Area", "mobileTemplate==null for template " + template); return null; } diff --git a/src/services/spawn/SpawnService.java b/src/services/spawn/SpawnService.java index f0c542fb..752a1171 100644 --- a/src/services/spawn/SpawnService.java +++ b/src/services/spawn/SpawnService.java @@ -374,7 +374,7 @@ public class SpawnService { } // Baby AI - creature.setAttachment("tamed", false); + creature.setAttachment("tamed", 0); AIActor actor = new AIActor(creature, creature.getPosition(), scheduler); creature.setAttachment("AI", actor); creature.setAttachment("radial_filename", "npc/untamed"); diff --git a/src/tools/CharonPacketLogger.java b/src/tools/CharonPacketLogger.java new file mode 100644 index 00000000..01da574b --- /dev/null +++ b/src/tools/CharonPacketLogger.java @@ -0,0 +1,60 @@ +package tools; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.nio.file.*; + +public class CharonPacketLogger extends Thread { + private static final CharonPacketLogger instance = new CharonPacketLogger(); + + private BlockingQueue itemsToLog = + new ArrayBlockingQueue(100); + + private static final String SHUTDOWN_REQ = "SHUTDOWN"; + private volatile boolean shuttingDown, loggerTerminated; + + public static CharonPacketLogger getLogger() { + return instance; + } + private CharonPacketLogger() { + start(); + } + + public void run() { + try { + String item; + while ((item = itemsToLog.take()) != SHUTDOWN_REQ) { + System.out.println(item); + } + } catch (InterruptedException iex) { + } finally { + loggerTerminated = true; + } + } + + public void log(String str) { + if (shuttingDown || loggerTerminated) return; + try { + itemsToLog.put(str); + } catch (InterruptedException iex) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unexpected interruption"); + } + } + + public void log(String str,Path path) { + if (shuttingDown || loggerTerminated) return; + try { + itemsToLog.put(str); + CharonPacketUtils.appendToLog(str, path); + } catch (InterruptedException iex) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unexpected interruption"); + } + } + + public void shutDown() throws InterruptedException { + shuttingDown = true; + itemsToLog.put(SHUTDOWN_REQ); + } +} diff --git a/src/tools/CharonPacketUtils.java b/src/tools/CharonPacketUtils.java index 7c091133..45c9ac03 100644 --- a/src/tools/CharonPacketUtils.java +++ b/src/tools/CharonPacketUtils.java @@ -1,6 +1,14 @@ package tools; +import java.io.BufferedWriter; +import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import main.NGECore; @@ -12,61 +20,58 @@ import org.apache.mina.core.buffer.IoBuffer; public class CharonPacketUtils { - private final static char[] HEX = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' - }; + static Path newFile = Paths.get("M:/PSWG/LOGS/PSWG_PacketLog.txt"); + private static boolean writeToFile = false; + private CharonPacketLogger logger; - public static String fromLong(long value) { - char[] hexs; - int i; - int c; - - hexs = new char[16]; - for (i = 0; i < 16; i++) { - c = (int)(value & 0xf); - hexs[16-i-1] = HEX[c]; - value = value >> 4; - } - return new String(hexs); - } - final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); - public static String bytesToHex(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - int v; - for ( int j = 0; j < bytes.length; j++ ) { - v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + public static void createLog(){ + + try { + Files.deleteIfExists(newFile); + newFile = Files.createFile(newFile); + } catch (IOException ex) { + System.out.println("Error creating file"); } - return new String(hexChars); + System.out.println(Files.exists(newFile)); + writeToFile=true; } - public static String byteToHex(byte bytes) { - char[] hexChars = new char[2]; - int v; - - v = bytes & 0xFF; - hexChars[2] = hexArray[v >>> 4]; - hexChars[2 + 1] = hexArray[v & 0x0F]; - - return new String(hexChars); + public static void appendToLog(String content, Path newFile){ + //Writing to file + try(BufferedWriter writer = Files.newBufferedWriter( + newFile, Charset.defaultCharset(),new OpenOption[] {StandardOpenOption.APPEND})){ + writer.append(content); + writer.append("\n"); + writer.flush(); + }catch(IOException exception){ + System.out.println("Error writing to file"); + } } public static void printAnalysis(IoBuffer pack){ + printAnalysis(pack, ""); + } + + public static void printAnalysis(IoBuffer pack, String packetName){ if (!NGECore.PACKET_DEBUG) return; + //appendToLog(packetName+" :"); + int hexLineNumber = 0; byte[] packArray = pack.array(); int lineCount = packArray.length/16; int actualIndex = 0; String buffString = ""; String asciiString = ""; + String logString = packetName+" :\n"; for (int lineIndex = 0; lineIndex <= lineCount; lineIndex++) { byte[] ASCIIArray = new byte[16]; + for (int x=0;x<16;x++) + ASCIIArray[x] = 32; + buffString = ""; asciiString = ""; // remaining bytes per line @@ -80,24 +85,57 @@ public class CharonPacketUtils { buffString += " "; else buffString += " "; - buffString += ((Integer.toString( ( packArray[actualIndex] & 0xff ) + 0x100, 16).substring( 1 )).toUpperCase() + ""); + String addString = ((Integer.toString( ( packArray[actualIndex] & 0xff ) + 0x100, 16).substring( 1 )).toUpperCase() + ""); + char character = addString.charAt(0); + if (character==0) + addString = " "; + buffString += addString; if (packArray[actualIndex] < 31 || packArray[actualIndex] == 13) - ASCIIArray[byteColumnIndex] = 32; + ASCIIArray[byteColumnIndex] = 46; else ASCIIArray[byteColumnIndex] = packArray[actualIndex]; + + + if (ASCIIArray[byteColumnIndex]==0) + ASCIIArray[byteColumnIndex]=32; actualIndex++; } try { - asciiString += new String(ASCIIArray, "US-ASCII"); + //asciiString += new String(ASCIIArray, "US-ASCII"); + asciiString += new String(ASCIIArray, "UTF-8"); + } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } - System.out.println(buffString + " " + asciiString); - - + asciiString.replaceAll(" ","."); + String number = Integer.toHexString(hexLineNumber); + if (number.length()<3) + number = "00" + number; + if (number.length()<4) + number = "0" + number; + + String lineNumberString = number + ": "; + hexLineNumber += 0x10; + //System.out.println(lineNumberString + buffString + " " + asciiString); + // if (writeToFile) + // appendToLog(lineNumberString + buffString + " " + asciiString); + + + logString += lineNumberString + buffString + " " + asciiString + "\n"; } - System.out.println("------------------------------------------------------------------------"); + + logString += "\n"; + + if (writeToFile){ + CharonPacketLogger logger = CharonPacketLogger.getLogger(); + logger.log(logString,newFile); + } + + //System.out.println("#########################################################################"); + System.out.println(""); + //if (writeToFile) + // appendToLog(""); } } diff --git a/src/tools/DevLog.java b/src/tools/DevLog.java new file mode 100644 index 00000000..5a5c1a64 --- /dev/null +++ b/src/tools/DevLog.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * 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 tools; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Charon + */ + +public class DevLog { + + private static int loggingSessionID=0; + private static Map debugEnable = new HashMap(); + static { + debugEnable.put("Charon",0); + debugEnable.put("YourNameHere",0); + } + + private static Map fileEnable = new HashMap(); + static { + fileEnable.put("Charon",0); + fileEnable.put("YourNameHere",0); + } + + + static Path newFile = Paths.get("src", "PSWG_DEVLOG.log"); + //static Path newFile = Paths.get("M:/PSWG/LOGS/PSWG_PacketLog.txt"); + private CharonPacketLogger logger; + + public DevLog(){ + + } + + public static void enableMe(){ + String OS_User = System.getProperty("user.name"); + if (debugEnable.keySet().contains(OS_User)){ + debugEnable.put(OS_User,1); + } + } + + public static void disableMe(){ + String OS_User = System.getProperty("user.name"); + if (debugEnable.keySet().contains(OS_User)){ + debugEnable.put(OS_User,0); + } + } + + public static void enableFileLogging(){ + String OS_User = System.getProperty("user.name"); + if (fileEnable.keySet().contains(OS_User)){ + fileEnable.put(OS_User,1); + } + } + + public static void disableFileLogging(){ + String OS_User = System.getProperty("user.name"); + if (fileEnable.keySet().contains(OS_User)){ + fileEnable.put(OS_User,0); + } + } + + public static boolean isFileLoggingEnabled(String developer){ + if (fileEnable.keySet().contains(developer)){ + if (fileEnable.get(developer)==1){ + return true; + } + } + return false; + } + + public static void debugout(String debugRequester, String testarea, String message){ + String developer = System.getProperty("user.name"); + String logMessage = ""; + if (!debugRequester.equals(developer)) + return; + if (debugEnable.keySet().contains(developer)){ + + if (debugEnable.get(developer)==1){ + logMessage = "Devlog->"+developer+"->"+testarea+"-> "+message; + DevLogQueuer logger = DevLogQueuer.getInstance(); + logger.log(logMessage); + } + } + + if (isFileLoggingEnabled(debugRequester)){ + if (loggingSessionID>0){ + DevLogQueuer logger = DevLogQueuer.getInstance(); + logger.log(logMessage,newFile); + + } else { + createLog(); + DevLogQueuer logger = DevLogQueuer.getInstance(); + logger.log(logMessage,newFile); + loggingSessionID++; + } + } + + } + + public static void createLog(){ + + try { + Date date = new Date(); + Calendar cal1 = Calendar.getInstance(); + cal1.setTimeInMillis(System.currentTimeMillis()); + SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-hh-mm-ss"); + String sDate=dateFormat.format(cal1.getTime()); + + String fileName = "PSWG_DEVLOG_"+sDate+".log"; + newFile = Paths.get("debuglogs", fileName); + Files.deleteIfExists(newFile); + newFile = Files.createFile(newFile); + createHeader(newFile); + } catch (IOException ex) { + System.out.println("Error creating file"); + } + } + + public static void appendToLog(String content, Path newFile){ + //Writing to file + try(BufferedWriter writer = Files.newBufferedWriter( + newFile, Charset.defaultCharset(),new OpenOption[] {StandardOpenOption.APPEND})){ + Date date = new Date(); + Calendar cal1 = Calendar.getInstance(); + cal1.setTimeInMillis(System.currentTimeMillis()); + SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss"); + String sDate=dateFormat.format(cal1.getTime()); + writer.append(sDate + " "); + writer.append(content); + writer.append("\n"); + writer.flush(); + }catch(IOException exception){ + System.out.println("Error writing to file"); + } + } + + public static void createHeader(Path newFile){ + //Writing to file + try(BufferedWriter writer = Files.newBufferedWriter( + newFile, Charset.defaultCharset(),new OpenOption[] {StandardOpenOption.APPEND})){ + Date date = new Date(); + Calendar cal1 = Calendar.getInstance(); + cal1.setTimeInMillis(System.currentTimeMillis()); + SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-hh:mm:ss"); + String sDate=dateFormat.format(cal1.getTime()); + String developer = System.getProperty("user.name"); + writer.append("*********************************************\n"); + writer.append("**** PROJECTSWG DEBUG LOG " + sDate + " ****\n"); + writer.append("*********************************************\n"); + writer.append("DEV: "+developer + "\n"); + writer.append("\n"); + writer.flush(); + }catch(IOException exception){ + System.out.println("Error writing to file"); + } + } + +} diff --git a/src/tools/DevLogQueuer.java b/src/tools/DevLogQueuer.java new file mode 100644 index 00000000..205eb8cc --- /dev/null +++ b/src/tools/DevLogQueuer.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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 tools; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.nio.file.*; + +/** + * @author Charon + */ + +public class DevLogQueuer extends Thread { + private static DevLogQueuer instance; + + private BlockingQueue itemsToLog = + new ArrayBlockingQueue(100); + + private static final String SHUTDOWN_REQ = "SHUTDOWN"; + private volatile boolean shuttingDown, loggerTerminated; + + + + public DevLogQueuer() { + if (instance!=null) + return; // Singleton + instance = this; + start(); + } + + public static DevLogQueuer getInstance() { + return instance; + } + + public static DevLogQueuer getLogger() { + return instance; + } + + public void run() { + try { + String item; + while ((item = itemsToLog.take()) != SHUTDOWN_REQ) { + System.err.println(item); + } + } catch (InterruptedException iex) { + } finally { + loggerTerminated = true; + } + } + + public void log(String str) { + + if (shuttingDown || loggerTerminated) return; + try { + itemsToLog.put(str); + } catch (InterruptedException iex) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unexpected interruption"); + } + } + + public void log(String str,Path path) { + if (shuttingDown || loggerTerminated) return; + try { + DevLog.appendToLog(str, path); + } catch (Exception iex) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unexpected interruption"); + } + } + + public void shutDown() throws InterruptedException { + shuttingDown = true; + itemsToLog.put(SHUTDOWN_REQ); + } +}