From 3a9305d0d9d66038fffe98d6cfdf1f99a84e664f Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 28 Sep 2021 08:33:25 +0200 Subject: [PATCH] Store merchant visitor history, and display it in the dialogue --- .../handlers/PlayerInteractionHandler.java | 11 ++-- src/main/java/server/maps/HiredMerchant.java | 62 ++++++++++++++----- src/main/java/tools/PacketCreator.java | 17 +++-- 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/server/channel/handlers/PlayerInteractionHandler.java b/src/main/java/net/server/channel/handlers/PlayerInteractionHandler.java index fa8d198d80..fbb8c757cc 100644 --- a/src/main/java/net/server/channel/handlers/PlayerInteractionHandler.java +++ b/src/main/java/net/server/channel/handlers/PlayerInteractionHandler.java @@ -312,8 +312,7 @@ public final class PlayerInteractionHandler extends AbstractPacketHandler { } else { chr.sendPacket(PacketCreator.getMiniRoomError(22)); } - } else if (ob instanceof HiredMerchant && chr.getHiredMerchant() == null) { - HiredMerchant merchant = (HiredMerchant) ob; + } else if (ob instanceof HiredMerchant merchant && chr.getHiredMerchant() == null) { merchant.visitShop(chr); } } @@ -686,9 +685,11 @@ public final class PlayerInteractionHandler extends AbstractPacketHandler { merchant.withdrawMesos(chr); } else if (mode == Action.VIEW_VISITORS.getCode()) { - List visitorNames = List.of("Dwayne", "Ruben", "Ada", "Clifton", "Beatrice", "Kent", "Max", - "Cecelia", "Edward", "Cory"); - c.sendPacket(PacketCreator.viewMerchantVisitors(visitorNames)); + HiredMerchant merchant = chr.getHiredMerchant(); + if (merchant == null || !merchant.isOwner(chr)) { + return; + } + c.sendPacket(PacketCreator.viewMerchantVisitorHistory(merchant.getVisitorHistory())); } else if (mode == Action.VIEW_BLACKLIST.getCode()) { List blacklistedNames = List.of("Blanca", "Betsy", "Kevin", "Rosa", "Evan", "Terence", "Cecilia", "Gayle", "Erma", "Dorothy", "Willis", "Alberta", "Marilyn", "Myron", "Sheryl", diff --git a/src/main/java/server/maps/HiredMerchant.java b/src/main/java/server/maps/HiredMerchant.java index 473bfb435f..c8010172f5 100644 --- a/src/main/java/server/maps/HiredMerchant.java +++ b/src/main/java/server/maps/HiredMerchant.java @@ -45,6 +45,8 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -57,6 +59,9 @@ import java.util.concurrent.locks.Lock; * @author Ronan - concurrency protection */ public class HiredMerchant extends AbstractMapObject { + private static final int VISITOR_HISTORY_LIMIT = 10; + private static final int BLACKLIST_LIMIT = 20; + private final int ownerId; private final int itemId; private final int mesos = 0; @@ -65,15 +70,20 @@ public class HiredMerchant extends AbstractMapObject { private final long start; private String ownerName = ""; private String description = ""; - private final Character[] visitors = new Character[3]; private final List items = new LinkedList<>(); private final List> messages = new LinkedList<>(); private final List sold = new LinkedList<>(); private final AtomicBoolean open = new AtomicBoolean(); private boolean published = false; private MapleMap map; + private final Visitor[] visitors = new Visitor[3]; + private final LinkedList visitorHistory = new LinkedList<>(); private final Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_MERCH, true); + private record Visitor(Character chr, Instant enteredAt) {} + + public record PastVisitor(String chrName, Duration visitDuration) {} + public HiredMerchant(final Character owner, String desc, int itemId) { this.setPosition(owner.getPosition()); this.start = System.currentTimeMillis(); @@ -96,9 +106,9 @@ public class HiredMerchant extends AbstractMapObject { } private void broadcastToVisitors(Packet packet) { - for (Character visitor : visitors) { + for (Visitor visitor : visitors) { if (visitor != null) { - visitor.sendPacket(packet); + visitor.chr.sendPacket(packet); } } } @@ -108,7 +118,7 @@ public class HiredMerchant extends AbstractMapObject { try { byte count = 0; if (this.isOpen()) { - for (Character visitor : visitors) { + for (Visitor visitor : visitors) { if (visitor != null) { count++; } @@ -128,7 +138,7 @@ public class HiredMerchant extends AbstractMapObject { try { int i = this.getFreeSlot(); if (i > -1) { - visitors[i] = visitor; + visitors[i] = new Visitor(visitor, Instant.now()); broadcastToVisitors(PacketCreator.hiredMerchantVisitorAdd(visitor, i + 1)); this.getMap().broadcastMessage(PacketCreator.updateHiredMerchantBox(this)); @@ -141,15 +151,18 @@ public class HiredMerchant extends AbstractMapObject { } } - public void removeVisitor(Character visitor) { + public void removeVisitor(Character chr) { visitorLock.lock(); try { - int slot = getVisitorSlot(visitor); + int slot = getVisitorSlot(chr); if (slot < 0) { //Not found return; } - if (visitors[slot] != null && visitors[slot].getId() == visitor.getId()) { + + Visitor visitor = visitors[slot]; + if (visitor != null && visitor.chr.getId() == chr.getId()) { visitors[slot] = null; + addVisitorToHistory(visitor); broadcastToVisitors(PacketCreator.hiredMerchantVisitorLeave(slot + 1)); this.getMap().broadcastMessage(PacketCreator.updateHiredMerchantBox(this)); } @@ -158,6 +171,14 @@ public class HiredMerchant extends AbstractMapObject { } } + private void addVisitorToHistory(Visitor visitor) { + Duration visitDuration = Duration.between(visitor.enteredAt, Instant.now()); + visitorHistory.addFirst(new PastVisitor(visitor.chr.getName(), visitDuration)); + while (visitorHistory.size() > VISITOR_HISTORY_LIMIT) { + visitorHistory.removeLast(); + } + } + public int getVisitorSlotThreadsafe(Character visitor) { visitorLock.lock(); try { @@ -169,7 +190,7 @@ public class HiredMerchant extends AbstractMapObject { private int getVisitorSlot(Character visitor) { for (int i = 0; i < 3; i++) { - if (visitors[i] != null && visitors[i].getId() == visitor.getId()) { + if (visitors[i] != null && visitors[i].chr.getId() == visitor.getId()) { return i; } } @@ -180,15 +201,15 @@ public class HiredMerchant extends AbstractMapObject { visitorLock.lock(); try { for (int i = 0; i < 3; i++) { - Character visitor = visitors[i]; + Visitor visitor = visitors[i]; if (visitor != null) { - visitor.setHiredMerchant(null); - - visitor.sendPacket(PacketCreator.leaveHiredMerchant(i + 1, 0x11)); - visitor.sendPacket(PacketCreator.hiredMerchantMaintenanceMessage()); - + final Character visitorChr = visitor.chr; + visitorChr.setHiredMerchant(null); + visitorChr.sendPacket(PacketCreator.leaveHiredMerchant(i + 1, 0x11)); + visitorChr.sendPacket(PacketCreator.hiredMerchantMaintenanceMessage()); visitors[i] = null; + addVisitorToHistory(visitor); } } @@ -498,12 +519,15 @@ public class HiredMerchant extends AbstractMapObject { return description; } - public Character[] getVisitors() { + public Character[] getVisitorCharacters() { visitorLock.lock(); try { Character[] copy = new Character[3]; for (int i = 0; i < visitors.length; i++) { - copy[i] = visitors[i]; + Visitor visitor = visitors[i]; + if (visitor != null) { + copy[i] = visitor.chr; + } } return copy; @@ -694,6 +718,10 @@ public class HiredMerchant extends AbstractMapObject { } } + public List getVisitorHistory() { + return Collections.unmodifiableList(visitorHistory); + } + public int getMapId() { return map.getId(); } diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index e3c372d070..ce2a95acf9 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -76,7 +76,6 @@ import java.sql.SQLException; import java.util.List; import java.util.*; import java.util.Map.Entry; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -5090,7 +5089,7 @@ public class PacketCreator { p.writeInt(hm.getItemId()); p.writeString("Hired Merchant"); - Character[] visitors = hm.getVisitors(); + Character[] visitors = hm.getVisitorCharacters(); for (int i = 0; i < 3; i++) { if (visitors[i] != null) { p.writeByte(i + 1); @@ -5205,16 +5204,16 @@ public class PacketCreator { } /** - * @param chrNames Merchant visitors. The first 10 names will be shown, - * everything beyond will layered over each other at the top of the window. + * @param pastVisitors Merchant visitors. The first 10 names will be shown, + * everything beyond will layered over each other at the top of the window. */ - public static Packet viewMerchantVisitors(List chrNames) { + public static Packet viewMerchantVisitorHistory(List pastVisitors) { final OutPacket p = OutPacket.create(SendOpcode.PLAYER_INTERACTION); p.writeByte(PlayerInteractionHandler.Action.VIEW_VISITORS.getCode()); - p.writeShort(chrNames.size()); - for (String chrName : chrNames) { - p.writeString(chrName); - p.writeInt((int) (TimeUnit.HOURS.toMillis(1) + TimeUnit.MINUTES.toMillis(23))); // milliseconds, displayed as hours+minutes + p.writeShort(pastVisitors.size()); + for (HiredMerchant.PastVisitor pastVisitor : pastVisitors) { + p.writeString(pastVisitor.chrName()); + p.writeInt((int) pastVisitor.visitDuration().toMillis()); // milliseconds, displayed as hours and minutes } return p; }