Store merchant visitor history, and display it in the dialogue

This commit is contained in:
P0nk
2021-09-28 08:33:25 +02:00
parent cc23d7734a
commit 3a9305d0d9
3 changed files with 59 additions and 31 deletions

View File

@@ -312,8 +312,7 @@ public final class PlayerInteractionHandler extends AbstractPacketHandler {
} else { } else {
chr.sendPacket(PacketCreator.getMiniRoomError(22)); chr.sendPacket(PacketCreator.getMiniRoomError(22));
} }
} else if (ob instanceof HiredMerchant && chr.getHiredMerchant() == null) { } else if (ob instanceof HiredMerchant merchant && chr.getHiredMerchant() == null) {
HiredMerchant merchant = (HiredMerchant) ob;
merchant.visitShop(chr); merchant.visitShop(chr);
} }
} }
@@ -686,9 +685,11 @@ public final class PlayerInteractionHandler extends AbstractPacketHandler {
merchant.withdrawMesos(chr); merchant.withdrawMesos(chr);
} else if (mode == Action.VIEW_VISITORS.getCode()) { } else if (mode == Action.VIEW_VISITORS.getCode()) {
List<String> visitorNames = List.of("Dwayne", "Ruben", "Ada", "Clifton", "Beatrice", "Kent", "Max", HiredMerchant merchant = chr.getHiredMerchant();
"Cecelia", "Edward", "Cory"); if (merchant == null || !merchant.isOwner(chr)) {
c.sendPacket(PacketCreator.viewMerchantVisitors(visitorNames)); return;
}
c.sendPacket(PacketCreator.viewMerchantVisitorHistory(merchant.getVisitorHistory()));
} else if (mode == Action.VIEW_BLACKLIST.getCode()) { } else if (mode == Action.VIEW_BLACKLIST.getCode()) {
List<String> blacklistedNames = List.of("Blanca", "Betsy", "Kevin", "Rosa", "Evan", "Terence", List<String> blacklistedNames = List.of("Blanca", "Betsy", "Kevin", "Rosa", "Evan", "Terence",
"Cecilia", "Gayle", "Erma", "Dorothy", "Willis", "Alberta", "Marilyn", "Myron", "Sheryl", "Cecilia", "Gayle", "Erma", "Dorothy", "Willis", "Alberta", "Marilyn", "Myron", "Sheryl",

View File

@@ -45,6 +45,8 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
@@ -57,6 +59,9 @@ import java.util.concurrent.locks.Lock;
* @author Ronan - concurrency protection * @author Ronan - concurrency protection
*/ */
public class HiredMerchant extends AbstractMapObject { 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 ownerId;
private final int itemId; private final int itemId;
private final int mesos = 0; private final int mesos = 0;
@@ -65,15 +70,20 @@ public class HiredMerchant extends AbstractMapObject {
private final long start; private final long start;
private String ownerName = ""; private String ownerName = "";
private String description = ""; private String description = "";
private final Character[] visitors = new Character[3];
private final List<PlayerShopItem> items = new LinkedList<>(); private final List<PlayerShopItem> items = new LinkedList<>();
private final List<Pair<String, Byte>> messages = new LinkedList<>(); private final List<Pair<String, Byte>> messages = new LinkedList<>();
private final List<SoldItem> sold = new LinkedList<>(); private final List<SoldItem> sold = new LinkedList<>();
private final AtomicBoolean open = new AtomicBoolean(); private final AtomicBoolean open = new AtomicBoolean();
private boolean published = false; private boolean published = false;
private MapleMap map; private MapleMap map;
private final Visitor[] visitors = new Visitor[3];
private final LinkedList<PastVisitor> visitorHistory = new LinkedList<>();
private final Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_MERCH, true); 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) { public HiredMerchant(final Character owner, String desc, int itemId) {
this.setPosition(owner.getPosition()); this.setPosition(owner.getPosition());
this.start = System.currentTimeMillis(); this.start = System.currentTimeMillis();
@@ -96,9 +106,9 @@ public class HiredMerchant extends AbstractMapObject {
} }
private void broadcastToVisitors(Packet packet) { private void broadcastToVisitors(Packet packet) {
for (Character visitor : visitors) { for (Visitor visitor : visitors) {
if (visitor != null) { if (visitor != null) {
visitor.sendPacket(packet); visitor.chr.sendPacket(packet);
} }
} }
} }
@@ -108,7 +118,7 @@ public class HiredMerchant extends AbstractMapObject {
try { try {
byte count = 0; byte count = 0;
if (this.isOpen()) { if (this.isOpen()) {
for (Character visitor : visitors) { for (Visitor visitor : visitors) {
if (visitor != null) { if (visitor != null) {
count++; count++;
} }
@@ -128,7 +138,7 @@ public class HiredMerchant extends AbstractMapObject {
try { try {
int i = this.getFreeSlot(); int i = this.getFreeSlot();
if (i > -1) { if (i > -1) {
visitors[i] = visitor; visitors[i] = new Visitor(visitor, Instant.now());
broadcastToVisitors(PacketCreator.hiredMerchantVisitorAdd(visitor, i + 1)); broadcastToVisitors(PacketCreator.hiredMerchantVisitorAdd(visitor, i + 1));
this.getMap().broadcastMessage(PacketCreator.updateHiredMerchantBox(this)); 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(); visitorLock.lock();
try { try {
int slot = getVisitorSlot(visitor); int slot = getVisitorSlot(chr);
if (slot < 0) { //Not found if (slot < 0) { //Not found
return; 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; visitors[slot] = null;
addVisitorToHistory(visitor);
broadcastToVisitors(PacketCreator.hiredMerchantVisitorLeave(slot + 1)); broadcastToVisitors(PacketCreator.hiredMerchantVisitorLeave(slot + 1));
this.getMap().broadcastMessage(PacketCreator.updateHiredMerchantBox(this)); 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) { public int getVisitorSlotThreadsafe(Character visitor) {
visitorLock.lock(); visitorLock.lock();
try { try {
@@ -169,7 +190,7 @@ public class HiredMerchant extends AbstractMapObject {
private int getVisitorSlot(Character visitor) { private int getVisitorSlot(Character visitor) {
for (int i = 0; i < 3; i++) { 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; return i;
} }
} }
@@ -180,15 +201,15 @@ public class HiredMerchant extends AbstractMapObject {
visitorLock.lock(); visitorLock.lock();
try { try {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
Character visitor = visitors[i]; Visitor visitor = visitors[i];
if (visitor != null) { if (visitor != null) {
visitor.setHiredMerchant(null); final Character visitorChr = visitor.chr;
visitorChr.setHiredMerchant(null);
visitor.sendPacket(PacketCreator.leaveHiredMerchant(i + 1, 0x11)); visitorChr.sendPacket(PacketCreator.leaveHiredMerchant(i + 1, 0x11));
visitor.sendPacket(PacketCreator.hiredMerchantMaintenanceMessage()); visitorChr.sendPacket(PacketCreator.hiredMerchantMaintenanceMessage());
visitors[i] = null; visitors[i] = null;
addVisitorToHistory(visitor);
} }
} }
@@ -498,12 +519,15 @@ public class HiredMerchant extends AbstractMapObject {
return description; return description;
} }
public Character[] getVisitors() { public Character[] getVisitorCharacters() {
visitorLock.lock(); visitorLock.lock();
try { try {
Character[] copy = new Character[3]; Character[] copy = new Character[3];
for (int i = 0; i < visitors.length; i++) { 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; return copy;
@@ -694,6 +718,10 @@ public class HiredMerchant extends AbstractMapObject {
} }
} }
public List<PastVisitor> getVisitorHistory() {
return Collections.unmodifiableList(visitorHistory);
}
public int getMapId() { public int getMapId() {
return map.getId(); return map.getId();
} }

View File

@@ -76,7 +76,6 @@ import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -5090,7 +5089,7 @@ public class PacketCreator {
p.writeInt(hm.getItemId()); p.writeInt(hm.getItemId());
p.writeString("Hired Merchant"); p.writeString("Hired Merchant");
Character[] visitors = hm.getVisitors(); Character[] visitors = hm.getVisitorCharacters();
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
if (visitors[i] != null) { if (visitors[i] != null) {
p.writeByte(i + 1); p.writeByte(i + 1);
@@ -5205,16 +5204,16 @@ public class PacketCreator {
} }
/** /**
* @param chrNames Merchant visitors. The first 10 names will be shown, * @param pastVisitors Merchant visitors. The first 10 names will be shown,
* everything beyond will layered over each other at the top of the window. * everything beyond will layered over each other at the top of the window.
*/ */
public static Packet viewMerchantVisitors(List<String> chrNames) { public static Packet viewMerchantVisitorHistory(List<HiredMerchant.PastVisitor> pastVisitors) {
final OutPacket p = OutPacket.create(SendOpcode.PLAYER_INTERACTION); final OutPacket p = OutPacket.create(SendOpcode.PLAYER_INTERACTION);
p.writeByte(PlayerInteractionHandler.Action.VIEW_VISITORS.getCode()); p.writeByte(PlayerInteractionHandler.Action.VIEW_VISITORS.getCode());
p.writeShort(chrNames.size()); p.writeShort(pastVisitors.size());
for (String chrName : chrNames) { for (HiredMerchant.PastVisitor pastVisitor : pastVisitors) {
p.writeString(chrName); p.writeString(pastVisitor.chrName());
p.writeInt((int) (TimeUnit.HOURS.toMillis(1) + TimeUnit.MINUTES.toMillis(23))); // milliseconds, displayed as hours+minutes p.writeInt((int) pastVisitor.visitDuration().toMillis()); // milliseconds, displayed as hours and minutes
} }
return p; return p;
} }