diff --git a/l2jserver2-common/src/main/java/com/l2jserver/model/AbstractModel.java b/l2jserver2-common/src/main/java/com/l2jserver/model/AbstractModel.java index 401e87266..f4dd9379d 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/model/AbstractModel.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/model/AbstractModel.java @@ -16,6 +16,10 @@ */ package com.l2jserver.model; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Preconditions; import com.l2jserver.model.id.ID; @@ -28,6 +32,8 @@ import com.l2jserver.model.id.ID; * @author Rogiel */ public abstract class AbstractModel> implements Model { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + /** * The object id */ @@ -58,6 +64,7 @@ public abstract class AbstractModel> implements Model { public void setObjectDesire(ObjectDesire desire) { if (desire == null) desire = ObjectDesire.NONE; + log.debug("{} set desire to {}", this, desire); this.desire = desire; } @@ -69,8 +76,10 @@ public abstract class AbstractModel> implements Model { @SuppressWarnings("javadoc") protected void desireUpdate() { if (this.desire != ObjectDesire.INSERT - && this.desire != ObjectDesire.DELETE) + && this.desire != ObjectDesire.DELETE) { + log.debug("{} desires an update", this); this.desire = ObjectDesire.UPDATE; + } } /** @@ -79,8 +88,10 @@ public abstract class AbstractModel> implements Model { */ @SuppressWarnings("javadoc") protected void desireInsert() { - if (this.desire != ObjectDesire.DELETE) + if (this.desire != ObjectDesire.DELETE) { + log.debug("{} desires an insert", this); this.desire = ObjectDesire.INSERT; + } } @Override diff --git a/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractJDBCDatabaseService.java b/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractJDBCDatabaseService.java index ed5d03a76..32f5c6ed5 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractJDBCDatabaseService.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/service/database/AbstractJDBCDatabaseService.java @@ -515,7 +515,7 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService this.parametize(st, object); log.debug("Sending query to database for {}", object); - rows += st.executeUpdate(); + rows = st.executeUpdate(); log.debug("Query inserted or updated {} rows for {}", rows, object); @@ -616,9 +616,7 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService continue; } if (obj instanceof Model) { - if (((Model) obj).getObjectDesire() == ObjectDesire.INSERT) { - ((Model) obj).setObjectDesire(ObjectDesire.NONE); - } + ((Model) obj).setObjectDesire(ObjectDesire.NONE); } log.debug("Mapper {} returned {}", mapper, obj); list.add(obj); @@ -695,9 +693,7 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService log.debug("Mapping row {} with {}", rs, mapper); final T object = mapper.map(rs); if (object instanceof Model) { - if (((Model) object).getObjectDesire() == ObjectDesire.INSERT) { - ((Model) object).setObjectDesire(ObjectDesire.NONE); - } + ((Model) object).setObjectDesire(ObjectDesire.NONE); } log.debug("Mapper {} returned {}", mapper, object); return object; diff --git a/l2jserver2-common/src/main/java/com/l2jserver/util/geometry/Point3D.java b/l2jserver2-common/src/main/java/com/l2jserver/util/geometry/Point3D.java index 26cd12ac7..09b568128 100644 --- a/l2jserver2-common/src/main/java/com/l2jserver/util/geometry/Point3D.java +++ b/l2jserver2-common/src/main/java/com/l2jserver/util/geometry/Point3D.java @@ -120,6 +120,17 @@ public class Point3D extends Point { return coordinate; } + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Point3D [" + coordinate.getX() + "," + coordinate.getY() + "," + + coordinate.getZ() + "," + angle + "]"; + } + /** * Creates a new instance from the 3 points and an angle * diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/SystemMessage.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/SystemMessage.java index 288179a58..d86f2854c 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/SystemMessage.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/SystemMessage.java @@ -204,7 +204,7 @@ public enum SystemMessage { * ID: 29
* Message: You have obtained $s2 $s1. */ - YOU_PICKED_UP_S1_S2(29), + YOU_PICKED_UP_S2_S1(29), /** * ID: 30
diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/codec/Lineage2PacketReader.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/codec/Lineage2PacketReader.java index b5be17f5c..2e3376ec5 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/codec/Lineage2PacketReader.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/codec/Lineage2PacketReader.java @@ -41,6 +41,7 @@ import com.l2jserver.game.net.packet.client.CM_CHAR_POSITION; import com.l2jserver.game.net.packet.client.CM_CHAR_REQ_INVENTORY; import com.l2jserver.game.net.packet.client.CM_CHAR_SELECT; import com.l2jserver.game.net.packet.client.CM_CHAT; +import com.l2jserver.game.net.packet.client.CM_DROP_ITEM; import com.l2jserver.game.net.packet.client.CM_ENTER_WORLD; import com.l2jserver.game.net.packet.client.CM_EXT_REQ_ALL_FORTRESS_INFO; import com.l2jserver.game.net.packet.client.CM_EXT_REQ_KEY_MAPPING; @@ -187,6 +188,8 @@ public class Lineage2PacketReader extends OneToOneDecoder { return CM_CHAR_OPEN_MAP.class; case CM_ATTACK.OPCODE: return CM_ATTACK.class; + case CM_DROP_ITEM.OPCODE: + return CM_DROP_ITEM.class; default: logger.warn("Unknown packet for 0x{}", Integer.toHexString(opcode)); break; diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/client/CM_CHAR_ACTION.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/client/CM_CHAR_ACTION.java index 143b9c56f..ab3626f32 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/client/CM_CHAR_ACTION.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/client/CM_CHAR_ACTION.java @@ -118,9 +118,7 @@ public class CM_CHAR_ACTION extends AbstractClientPacket { final NPC npc = ((NPCID) id).getObject(); try { npcService.action(npc, conn.getCharacter(), action); - } catch (ActionServiceException e) { - conn.sendActionFailed(); - } catch (CannotSetTargetServiceException e) { + } catch (ActionServiceException | CannotSetTargetServiceException e) { conn.sendActionFailed(); } } else if (id instanceof ItemID) { @@ -133,7 +131,7 @@ public class CM_CHAR_ACTION extends AbstractClientPacket { } else { // update only conn.updateInventoryItems(stackItem); } - conn.sendSystemMessage(SystemMessage.YOU_PICKED_UP_S1_S2, Long + conn.sendSystemMessage(SystemMessage.YOU_PICKED_UP_S2_S1, Long .toString(item.getCount()), item.getTemplate() .getName()); conn.sendActionFailed(); diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/client/CM_DROP_ITEM.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/client/CM_DROP_ITEM.java new file mode 100644 index 000000000..bce690779 --- /dev/null +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/client/CM_DROP_ITEM.java @@ -0,0 +1,122 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * l2jserver2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with l2jserver2. If not, see . + */ +package com.l2jserver.game.net.packet.client; + +import org.jboss.netty.buffer.ChannelBuffer; + +import com.google.inject.Inject; +import com.l2jserver.game.net.Lineage2Client; +import com.l2jserver.game.net.SystemMessage; +import com.l2jserver.game.net.packet.AbstractClientPacket; +import com.l2jserver.model.id.object.ItemID; +import com.l2jserver.model.id.object.provider.ItemIDProvider; +import com.l2jserver.model.world.Item; +import com.l2jserver.service.game.item.ItemAlreadyOnGroundServiceException; +import com.l2jserver.service.game.item.ItemService; +import com.l2jserver.service.game.item.NotEnoughItemsServiceException; +import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException; +import com.l2jserver.service.game.spawn.SpawnPointNotFoundServiceException; +import com.l2jserver.util.geometry.Point3D; + +/** + * This packet drops items on the ground. + * + * @author Rogiel + */ +public class CM_DROP_ITEM extends AbstractClientPacket { + /** + * The packet OPCODE + */ + public static final int OPCODE = 0x17; + + /** + * The {@link ItemService} + */ + private final ItemService itemService; + private final ItemIDProvider itemIdProvider; + + /** + * The item ID + */ + private int objectId; + /** + * The number of items to be dropped + */ + private long count; + /** + * The location to be dropped + */ + private Point3D point; + + /** + * @param itemService + * the item service + * @param itemIdProvider + * the item id provider + */ + @Inject + public CM_DROP_ITEM(ItemService itemService, ItemIDProvider itemIdProvider) { + this.itemService = itemService; + this.itemIdProvider = itemIdProvider; + } + + @Override + public void read(Lineage2Client conn, ChannelBuffer buffer) { + objectId = buffer.readInt(); + count = buffer.readLong(); + point = Point3D.fromXYZ(buffer.readInt(), buffer.readInt(), + buffer.readInt()); + } + + @Override + public void process(final Lineage2Client conn) { + final ItemID id = itemIdProvider.resolveID(objectId); + final Item item = id.getObject(); + + if (item == null) { + conn.sendActionFailed(); + return; + } + if (!conn.getCharacterID().equals(item.getOwnerID())) { + conn.sendActionFailed(); + return; + } + + try { + final Item dropped = itemService.drop(item, count, point, + conn.getCharacter()); + if (dropped.equals(item)) { + conn.removeInventoryItems(dropped); + } else { + conn.updateInventoryItems(item); + } + if (dropped.getCount() == 1) { + conn.sendSystemMessage(SystemMessage.YOU_DROPPED_S1, item + .getTemplate().getName()); + } else { + conn.sendSystemMessage(SystemMessage.DROPPED_S1_S2, item + .getTemplate().getName(), Long.toString(dropped + .getCount())); + } + } catch (ItemAlreadyOnGroundServiceException + | AlreadySpawnedServiceException + | SpawnPointNotFoundServiceException + | NotEnoughItemsServiceException e) { + conn.sendActionFailed(); + } + } +} diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/server/SM_CHAR_INVENTORY.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/server/SM_CHAR_INVENTORY.java index e8b7efeda..a9363efe0 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/server/SM_CHAR_INVENTORY.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/game/net/packet/server/SM_CHAR_INVENTORY.java @@ -59,6 +59,7 @@ public class SM_CHAR_INVENTORY extends AbstractServerPacket { int slot = 0; for (Item item : inventory) { if (item.getLocation() == ItemLocation.WAREHOUSE + || item.getLocation() == ItemLocation.GROUND || item.getLocation() == null) { continue; } diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/Item.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/Item.java index a1ee76749..dfc869af8 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/Item.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/Item.java @@ -17,21 +17,22 @@ package com.l2jserver.model.world; import com.l2jserver.model.id.object.CharacterID; +import com.l2jserver.model.id.object.ItemID; import com.l2jserver.model.id.template.ItemTemplateID; import com.l2jserver.model.template.item.ItemTemplate; -import com.l2jserver.model.world.character.CharacterInventory.ItemLocation; import com.l2jserver.model.world.character.CharacterInventory.InventoryPaperdoll; +import com.l2jserver.model.world.character.CharacterInventory.ItemLocation; /** * This class represents an {@link Item} in the Lineage II World. The item can * be: *
    *
  • In the {@link L2Character character} inventory: location - * is {@link ItemLocation#INVENTORY}, coordinate and - * paperdoll are null.
  • + * is {@link ItemLocation#INVENTORY}, coordinate and paperdoll + * are null. *
  • In the {@link L2Character character} warehouse: location - * is {@link ItemLocation#WAREHOUSE}, coordinate and - * paperdoll are null.
  • + * is {@link ItemLocation#WAREHOUSE}, coordinate and paperdoll + * are null. *
  • Equipped by the {@link L2Character character}: location * is {@link ItemLocation#PAPERDOLL}, paperdoll is not null and * coordinate is null.
  • @@ -58,7 +59,7 @@ public class Item extends PositionableObject { /** * Inventory location of this item */ - private ItemLocation location; + private ItemLocation location = ItemLocation.INVENTORY; /** * Paperdoll slot for this item */ @@ -156,4 +157,9 @@ public class Item extends PositionableObject { desireUpdate(); this.ownerID = ownerID; } + + @Override + public ItemID getID() { + return (ItemID) super.getID(); + } } diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/character/CharacterInventory.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/character/CharacterInventory.java index 49c8081c1..3e9a8f80f 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/character/CharacterInventory.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/character/CharacterInventory.java @@ -16,12 +16,13 @@ */ package com.l2jserver.model.world.character; -import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Set; import com.l2jserver.model.id.object.ItemID; +import com.l2jserver.model.id.template.ItemTemplateID; import com.l2jserver.model.world.Item; import com.l2jserver.model.world.L2Character; import com.l2jserver.util.factory.CollectionFactory; @@ -76,6 +77,23 @@ public class CharacterInventory implements Iterable { return null; } + /** + * Removes several items from the players inventory. + * + * @param items + * the items to be removed + * @return the items that were effectivelly removed + */ + public Item[] remove(Item... items) { + final List removedItems = CollectionFactory.newList(); + for (final Item item : items) { + if (this.items.remove(item)) { + removedItems.add(item); + } + } + return removedItems.toArray(new Item[removedItems.size()]); + } + /** * Removes all items from the given {@link ItemID} * @@ -84,7 +102,7 @@ public class CharacterInventory implements Iterable { * @return an array of all items removed. Can never be null. */ public Item[] remove(ItemID itemID) { - final ArrayList removedItems = new ArrayList(); + final List removedItems = CollectionFactory.newList(); for (final Item item : items) { if (item.getID().equals(itemID)) { items.remove(item); @@ -94,6 +112,23 @@ public class CharacterInventory implements Iterable { return removedItems.toArray(new Item[removedItems.size()]); } + /** + * Returns all items from the given {@link ItemTemplateID} + * + * @param itemTemplateID + * the {@link ItemTemplateID} + * @return an array of all items with the given ID + */ + public Item[] getItems(ItemTemplateID itemTemplateID) { + final List allItems = CollectionFactory.newList(); + for (final Item item : items) { + if (item.getTemplateID().equals(itemTemplateID)) { + allItems.add(item); + } + } + return allItems.toArray(new Item[allItems.size()]); + } + /** * Get the item in the given paperdoll slot * diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/item/ItemDropEvent.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/item/ItemDropEvent.java index 7b46b5774..33d8898e1 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/item/ItemDropEvent.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/item/ItemDropEvent.java @@ -19,20 +19,19 @@ package com.l2jserver.model.world.item; import com.l2jserver.model.id.ObjectID; import com.l2jserver.model.world.Actor; import com.l2jserver.model.world.Item; -import com.l2jserver.model.world.Player; import com.l2jserver.model.world.WorldObject; -import com.l2jserver.model.world.player.event.PlayerEvent; +import com.l2jserver.model.world.actor.event.ActorEvent; /** * Event dispatched once an {@link Item} has been dropped on the ground. * * @author Rogiel */ -public class ItemDropEvent implements ItemEvent, PlayerEvent { +public class ItemDropEvent implements ItemEvent, ActorEvent { /** - * The dropping player + * The dropping actor */ - private final Player player; + private final Actor actor; /** * The item dropped */ @@ -41,13 +40,13 @@ public class ItemDropEvent implements ItemEvent, PlayerEvent { /** * Creates a new instance of this event * - * @param player - * the dropping player + * @param actor + * the dropping actor * @param item * the dropped item */ - public ItemDropEvent(Player player, Item item) { - this.player = player; + public ItemDropEvent(Actor actor, Item item) { + this.actor = actor; this.item = item; } @@ -56,11 +55,6 @@ public class ItemDropEvent implements ItemEvent, PlayerEvent { return item; } - @Override - public Player getPlayer() { - return player; - } - @Override public Item getItem() { return item; @@ -68,7 +62,7 @@ public class ItemDropEvent implements ItemEvent, PlayerEvent { @Override public Actor getActor() { - return player; + return actor; } @Override diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/item/ItemPickUpEvent.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/item/ItemPickUpEvent.java index 1623b224a..117d417cd 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/item/ItemPickUpEvent.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/model/world/item/ItemPickUpEvent.java @@ -38,6 +38,10 @@ public class ItemPickUpEvent implements ItemEvent, CharacterEvent { * The item picked up */ private final Item item; + /** + * The new item + */ + private final Item newItem; /** * Creates a new instance of this event @@ -46,10 +50,19 @@ public class ItemPickUpEvent implements ItemEvent, CharacterEvent { * the picking up character * @param item * the picked up item + * @param newItem the new {@link Item} */ - public ItemPickUpEvent(L2Character character, Item item) { + public ItemPickUpEvent(L2Character character, Item item, Item newItem) { this.character = character; this.item = item; + this.newItem = newItem; + } + + /** + * @return the new {@link Item} + */ + public Item getNewItem() { + return newItem; } @Override diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/database/jdbc/JDBCItemDAO.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/database/jdbc/JDBCItemDAO.java index beaabef7b..cbed4d037 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/database/jdbc/JDBCItemDAO.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/database/jdbc/JDBCItemDAO.java @@ -184,12 +184,13 @@ public abstract class JDBCItemDAO extends AbstractJDBCDAO @Override protected String query() { return "SELECT * FROM `" + TABLE + "` WHERE `" + CHAR_ID - + "` = ?"; + + "` = ? AND `location` = ?"; } @Override protected void parametize(PreparedStatement st) throws SQLException { st.setInt(1, character.getID().getID()); + st.setString(2, ItemLocation.INVENTORY.name()); } @Override @@ -239,23 +240,47 @@ public abstract class JDBCItemDAO extends AbstractJDBCDAO @Override public boolean insert(Item item) { - throw new UnsupportedOperationException( - "Saving items is not yet implemented!"); + return database.query(new InsertUpdateQuery(item) { + @Override + protected String query() { + return "INSERT INTO `" + TABLE + "` (`" + ITEM_ID + "`,`" + + TEMPLATE_ID + "`,`" + CHAR_ID + "`,`" + LOCATION + + "`,`" + PAPERDOLL + "`,`" + COUNT + "`,`" + COORD_X + + "`,`" + COORD_Y + "`,`" + COORD_Z + + "`) VALUES(?,?,?,?,?,?,?,?,?)"; + } + + @Override + protected void parametize(PreparedStatement st, Item item) + throws SQLException { + int i = 1; + + st.setInt(i++, item.getID().getID()); + st.setInt(i++, item.getTemplateID().getID()); + if (item.getOwnerID() != null) { + st.setInt(i++, item.getOwnerID().getID()); + } else { + st.setNull(i++, Types.INTEGER); + } + st.setString(i++, item.getLocation().name()); + st.setString(i++, (item.getPaperdoll() != null ? item + .getPaperdoll().name() : null)); + st.setLong(i++, item.getCount()); + if (item.getPoint() != null) { + st.setInt(i++, item.getPoint().getX()); + st.setInt(i++, item.getPoint().getY()); + st.setInt(i++, item.getPoint().getZ()); + } else { + st.setNull(i++, Types.INTEGER); + st.setNull(i++, Types.INTEGER); + st.setNull(i++, Types.INTEGER); + } + } + }) > 0; } @Override public boolean update(Item item) { - // public static final String ITEM_ID = "item_id"; - // public static final String TEMPLATE_ID = "template_id"; - // public static final String CHAR_ID = JDBCCharacterDAO.CHAR_ID; - // - // public static final String LOCATION = "location"; - // public static final String PAPERDOLL = "paperdoll"; - // public static final String COUNT = "count"; - // public static final String COORD_X = "coord_x"; - // public static final String COORD_Y = "coord_y"; - // public static final String COORD_Z = "coord_z"; - return database.query(new InsertUpdateQuery(item) { @Override protected String query() { @@ -285,16 +310,15 @@ public abstract class JDBCItemDAO extends AbstractJDBCDAO st.setInt(i++, item.getPoint().getY()); st.setInt(i++, item.getPoint().getZ()); } else { - st.setInt(i++, 0); - st.setInt(i++, 0); - st.setInt(i++, 0); + st.setNull(i++, Types.INTEGER); + st.setNull(i++, Types.INTEGER); + st.setNull(i++, Types.INTEGER); } // WHERE st.setInt(i++, item.getID().getID()); } }) > 0; - } @Override diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/ItemService.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/ItemService.java index 5e1782eee..9da30806f 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/ItemService.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/ItemService.java @@ -52,6 +52,33 @@ public interface ItemService extends Service { Item action(Item item, L2Character character, CharacterAction action) throws ItemNotOnGroundServiceException, NotSpawnedServiceException; + /** + * Splits this item in two pieces + * + * @param item + * the item to be splitted + * @param count + * the amount of items that will be decreased from the argument + * {@link Item} and added to the result one. + * @return the splitted item + * @throws NotEnoughItemsServiceException + * if count is bigger than {@link Item#getCount()}. + */ + Item split(Item item, long count) throws NotEnoughItemsServiceException; + + /** + * Stacks several items into a single one. Items must be provided by the + * same template! + * + * @param items + * the items to be stacked + * @return the stacked item (it may be a totally new item or it might reuse + * another one) + * @throws NonStackableItemsServiceException + * if the item templates says they are not stackable + */ + Item stack(Item... items) throws NonStackableItemsServiceException; + /** * Picks up an dropped item and places it into another players inventory * @@ -68,6 +95,35 @@ public interface ItemService extends Service { Item pickUp(Item item, L2Character character) throws ItemNotOnGroundServiceException, NotSpawnedServiceException; + /** + * Drops an item on the ground. If actor is not + * null he is flagged as the dropper actor. + * + * @param item + * the item + * @param count + * the number of items to be dropped. Will cause an split. + * @param point + * the drop point. Can be null if + * {@link Item#getPoint()} is set. + * @param actor + * the dropping actor. Can be null. + * @return item or another instance if the item was splitted. + * @throws ItemAlreadyOnGroundServiceException + * if the item is already dropped + * @throws AlreadySpawnedServiceException + * if the item is already spawned in the {@link WorldService} + * @throws SpawnPointNotFoundServiceException + * if point was null and + * {@link Item#getPoint()} was not set. + * @throws NotEnoughItemsServiceException + * if count is bigger than {@link Item#getCount()}. + */ + Item drop(Item item, long count, Point3D point, Actor actor) + throws ItemAlreadyOnGroundServiceException, + AlreadySpawnedServiceException, SpawnPointNotFoundServiceException, + NotEnoughItemsServiceException; + /** * Drops an item on the ground. If actor is not * null he is flagged as the dropper actor. @@ -86,8 +142,11 @@ public interface ItemService extends Service { * @throws SpawnPointNotFoundServiceException * if point was null and * {@link Item#getPoint()} was not set. + * @throws NotEnoughItemsServiceException + * if count is bigger than {@link Item#getCount()}. */ void drop(Item item, Point3D point, Actor actor) throws ItemAlreadyOnGroundServiceException, - AlreadySpawnedServiceException, SpawnPointNotFoundServiceException; + AlreadySpawnedServiceException, SpawnPointNotFoundServiceException, + NotEnoughItemsServiceException; } diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/ItemServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/ItemServiceImpl.java index b0de05953..e067b73ab 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/ItemServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/ItemServiceImpl.java @@ -16,15 +16,22 @@ */ package com.l2jserver.service.game.item; +import java.util.Arrays; import java.util.List; +import com.google.common.base.Preconditions; import com.google.inject.Inject; import com.l2jserver.game.net.packet.client.CM_CHAR_ACTION.CharacterAction; +import com.l2jserver.model.Model.ObjectDesire; import com.l2jserver.model.dao.ItemDAO; +import com.l2jserver.model.id.object.ItemID; +import com.l2jserver.model.id.object.provider.ItemIDProvider; +import com.l2jserver.model.id.template.ItemTemplateID; import com.l2jserver.model.world.Actor; import com.l2jserver.model.world.Item; import com.l2jserver.model.world.L2Character; import com.l2jserver.model.world.character.CharacterInventory.ItemLocation; +import com.l2jserver.model.world.item.ItemDropEvent; import com.l2jserver.model.world.item.ItemPickUpEvent; import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService.Depends; @@ -56,6 +63,10 @@ public class ItemServiceImpl extends AbstractService implements ItemService { * The {@link WorldService} event dispatcher */ private final WorldEventDispatcher eventDispatcher; + /** + * The {@link ItemID} provider + */ + private final ItemIDProvider itemIdProvider; /** * All items on the ground persisted to the database @@ -69,13 +80,16 @@ public class ItemServiceImpl extends AbstractService implements ItemService { * the spawn service * @param eventDispatcher * the world service event dispatcher + * @param itemIdProvider + * the {@link ItemID} provider */ @Inject private ItemServiceImpl(ItemDAO itemDao, SpawnService spawnService, - WorldEventDispatcher eventDispatcher) { + WorldEventDispatcher eventDispatcher, ItemIDProvider itemIdProvider) { this.itemDao = itemDao; this.spawnService = spawnService; this.eventDispatcher = eventDispatcher; + this.itemIdProvider = itemIdProvider; } @Override @@ -104,6 +118,44 @@ public class ItemServiceImpl extends AbstractService implements ItemService { return item; } + @Override + public Item split(Item item, long count) + throws NotEnoughItemsServiceException { + if (item.getCount() < count) + throw new NotEnoughItemsServiceException(); + if (item.getCount() == count) + return item; + + final Item splitItem = item.getTemplate().create(); + splitItem.setID(itemIdProvider.createID()); + splitItem.setCount(count); + item.setCount(item.getCount() - count); + + splitItem.setObjectDesire(ObjectDesire.INSERT); + + return splitItem; + } + + @Override + public Item stack(Item... items) throws NonStackableItemsServiceException { + Preconditions.checkState(items.length >= 2, + "items length must be 2 or greater"); + // TODO implement real item stacking + + final ItemTemplateID templateID = items[0].getTemplateID(); + for (final Item item : items) { + if (!item.getTemplateID().equals(templateID)) + throw new NonStackableItemsServiceException(); + } + + final Item item = items[0]; + for (int i = 1; i < items.length; i++) { + item.setCount(item.getCount() + items[i].getCount()); + } + + return item; + } + @Override public Item pickUp(Item item, L2Character character) throws ItemNotOnGroundServiceException, NotSpawnedServiceException { @@ -111,15 +163,75 @@ public class ItemServiceImpl extends AbstractService implements ItemService { if (item.getLocation() != ItemLocation.GROUND) throw new ItemNotOnGroundServiceException(); + final Item originalItem = item; + item.setLocation(ItemLocation.INVENTORY); item.setPaperdoll(null); item.setOwnerID(character.getID()); - character.getInventory().add(item); + final Item[] items = character.getInventory().getItems( + item.getTemplateID()); + if (items.length != 0) { + Item[] stackItems = Arrays.copyOf(items, items.length + 1); + stackItems[items.length] = item; + try { + item = stack(stackItems); + Item[] removedItems = character.getInventory().remove( + stackItems); + for (final Item removeItem : removedItems) { + if (!removeItem.equals(item)) { + itemDao.delete(removeItem); + itemIdProvider.destroy(removeItem.getID()); + } + } + character.getInventory().add(item); + } catch (NonStackableItemsServiceException e) { + character.getInventory().add(item); + } + } else { + character.getInventory().add(item); + } - items.remove(item); - - spawnService.unspawn(item); - eventDispatcher.dispatch(new ItemPickUpEvent(character, item)); + character.getInventory().add(item); + this.items.remove(item); + + itemDao.save(item); + spawnService.unspawn(originalItem); + eventDispatcher.dispatch(new ItemPickUpEvent(character, + originalItem, item)); + + return item; + } + } + + @Override + public Item drop(Item item, long count, Point3D point, Actor actor) + throws SpawnPointNotFoundServiceException, + ItemAlreadyOnGroundServiceException, + AlreadySpawnedServiceException, NotEnoughItemsServiceException { + synchronized (item) { + if (item.getLocation() == ItemLocation.GROUND) + throw new AlreadySpawnedServiceException(); + + final Item sourceItem = item; + item = split(sourceItem, count); + + item.setLocation(ItemLocation.GROUND); + item.setPaperdoll(null); + + spawnService.spawn(item, point); + eventDispatcher.dispatch(new ItemDropEvent(actor, item)); + + if (actor instanceof L2Character) { + if (sourceItem.equals(item)) { + ((L2Character) actor).getInventory().remove(item); + } + } + + itemDao.save(item); + if (!item.equals(sourceItem)) { + itemDao.save(sourceItem); + } + items.add(item); return item; } @@ -128,18 +240,9 @@ public class ItemServiceImpl extends AbstractService implements ItemService { @Override public void drop(Item item, Point3D point, Actor actor) throws SpawnPointNotFoundServiceException, - ItemAlreadyOnGroundServiceException, AlreadySpawnedServiceException { - synchronized (item) { - if (item.getLocation() == ItemLocation.GROUND) - throw new AlreadySpawnedServiceException(); - - item.setLocation(ItemLocation.GROUND); - item.setPaperdoll(null); - // point will be set here - spawnService.spawn(item, point); - - items.add(item); - } + ItemAlreadyOnGroundServiceException, + AlreadySpawnedServiceException, NotEnoughItemsServiceException { + drop(item, item.getCount(), point, actor); } @Override diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/NonStackableItemsServiceException.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/NonStackableItemsServiceException.java new file mode 100644 index 000000000..85ae18d95 --- /dev/null +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/NonStackableItemsServiceException.java @@ -0,0 +1,59 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * l2jserver2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with l2jserver2. If not, see . + */ +package com.l2jserver.service.game.item; + +/** + * @author Rogiel + */ +public class NonStackableItemsServiceException extends ItemServiceException { + /** + * Java Serialization API ID + */ + private static final long serialVersionUID = 1L; + + /** + * Creates a new instance + */ + public NonStackableItemsServiceException() { + } + + /** + * @param message + * the message + * @param cause + * the cause + */ + public NonStackableItemsServiceException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param message + * the message + */ + public NonStackableItemsServiceException(String message) { + super(message); + } + + /** + * @param cause + * the cause + */ + public NonStackableItemsServiceException(Throwable cause) { + super(cause); + } +} diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/NotEnoughItemsServiceException.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/NotEnoughItemsServiceException.java new file mode 100644 index 000000000..da6d5971c --- /dev/null +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/item/NotEnoughItemsServiceException.java @@ -0,0 +1,59 @@ +/* + * This file is part of l2jserver2 . + * + * l2jserver2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * l2jserver2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with l2jserver2. If not, see . + */ +package com.l2jserver.service.game.item; + +/** + * @author Rogiel + */ +public class NotEnoughItemsServiceException extends ItemServiceException { + /** + * Java Serialization API ID + */ + private static final long serialVersionUID = 1L; + + /** + * Creates a new instance + */ + public NotEnoughItemsServiceException() { + } + + /** + * @param message + * the message + * @param cause + * the cause + */ + public NotEnoughItemsServiceException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param message + * the message + */ + public NotEnoughItemsServiceException(String message) { + super(message); + } + + /** + * @param cause + * the cause + */ + public NotEnoughItemsServiceException(Throwable cause) { + super(cause); + } +} diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/spawn/SpawnService.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/spawn/SpawnService.java index e09703cc5..67171ffd3 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/spawn/SpawnService.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/game/spawn/SpawnService.java @@ -30,7 +30,10 @@ import com.l2jserver.model.world.player.event.PlayerTeleportedEvent; import com.l2jserver.service.Service; import com.l2jserver.service.core.threading.AsyncFuture; import com.l2jserver.service.core.threading.ThreadService; +import com.l2jserver.service.game.character.CharacterService; +import com.l2jserver.service.game.item.ItemService; import com.l2jserver.service.game.npc.NPCService; +import com.l2jserver.service.game.world.WorldService; import com.l2jserver.util.geometry.Coordinate; import com.l2jserver.util.geometry.Point3D; @@ -52,6 +55,12 @@ import com.l2jserver.util.geometry.Point3D; * do, the {@link NPC} will not be respawned. The only possible way to respawn * it is through an forced spawn (manual) or server restart. See * {@link NPCService} if you wish to correctly unspawn an {@link NPC}. + *

    + * Also note that this service is a low level implementation. In most cases, it + * might be more suitable the usage of high-level services, like + * {@link ItemService}, {@link NPCService} or {@link CharacterService}, since + * they can provide more security and dispatch more events to the + * {@link WorldService}. * * @author Rogiel */ diff --git a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastServiceImpl.java b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastServiceImpl.java index b7caa4170..1be7abdd7 100644 --- a/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastServiceImpl.java +++ b/l2jserver2-gameserver/src/main/java/com/l2jserver/service/network/broadcast/BroadcastServiceImpl.java @@ -46,6 +46,7 @@ import com.l2jserver.model.world.character.event.CharacterLeaveWorldEvent; import com.l2jserver.model.world.character.event.CharacterMoveEvent; import com.l2jserver.model.world.character.event.CharacterRunningEvent; import com.l2jserver.model.world.character.event.CharacterWalkingEvent; +import com.l2jserver.model.world.item.ItemDropEvent; import com.l2jserver.model.world.item.ItemPickUpEvent; import com.l2jserver.model.world.npc.event.NPCSpawnEvent; import com.l2jserver.model.world.player.event.PlayerTeleportedEvent; @@ -115,7 +116,7 @@ public class BroadcastServiceImpl extends AbstractService implements @Override protected boolean dispatch(WorldEvent e, PositionableObject object) { log.debug("Broadcast event received: {}", e); - if (e instanceof NPCSpawnEvent) { + if (e instanceof NPCSpawnEvent || e instanceof ItemDropEvent) { broadcast(conn, e.getObject()); } else if (e instanceof CharacterMoveEvent) { final CharacterMoveEvent evt = (CharacterMoveEvent) e; @@ -129,7 +130,7 @@ public class BroadcastServiceImpl extends AbstractService implements || e instanceof ActorUnspawnEvent || e instanceof ItemPickUpEvent) { // object is now out of sight - //FIXME pick up animation is not happening + // FIXME pick up animation is not happening conn.write(new SM_OBJECT_REMOVE(object)); } else if (e instanceof CharacterWalkingEvent) { conn.write(new SM_MOVE_TYPE(((CharacterWalkingEvent) e)