Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32c4f2239d | ||
|
|
404c00c2bf | ||
|
|
9def444442 | ||
|
|
771b69d151 | ||
|
|
37a9a4121f | ||
|
|
4731c0c60d | ||
|
|
65111ae209 | ||
|
|
2a460de911 | ||
|
|
cee82a08ba | ||
|
|
387437cada | ||
|
|
af14da987e | ||
|
|
389b3ad2a4 | ||
|
|
5f1f5b7dcd | ||
|
|
7e3be4c45d | ||
|
|
c82881e6f2 | ||
|
|
6be1fabc55 | ||
|
|
4d480660b5 | ||
|
|
1f4ce98998 | ||
|
|
605f2e212e | ||
|
|
188eb74a70 | ||
|
|
2d7d113458 |
6
.github/workflows/bump-version.yml
vendored
6
.github/workflows/bump-version.yml
vendored
@@ -1,4 +1,5 @@
|
|||||||
# This workflow will tag the merge commit when merging a PR into the master branch.
|
# This workflow will tag the merge commit when merging a PR into the master branch.
|
||||||
|
# Add "#patch", "#minor", or "#major" at end of the merge commit subject to dictate the type of bump.
|
||||||
|
|
||||||
name: Bump version
|
name: Bump version
|
||||||
on:
|
on:
|
||||||
@@ -16,7 +17,8 @@ jobs:
|
|||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Bump version and push tag
|
- name: Bump version and push tag
|
||||||
uses: anothrNick/github-tag-action@v1
|
uses: anothrNick/github-tag-action@1.55.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
WITH_V: true
|
WITH_V: true
|
||||||
|
BRANCH_HISTORY: last
|
||||||
@@ -211,12 +211,17 @@ To launch the server, you may either:
|
|||||||
---
|
---
|
||||||
### Getting into the game
|
### Getting into the game
|
||||||
|
|
||||||
If you ran the admin sql script, there already exists an account in your database with an admin character on it. You don't need to change its GM level. Log in using these credentials:
|
If you ran the admin sql script, there already exists an account in the database with an admin character on it (GM level 6).
|
||||||
|
|
||||||
|
Log in using these credentials:
|
||||||
* Username: "admin"
|
* Username: "admin"
|
||||||
* Password: "admin"
|
* Password: "admin"
|
||||||
* Pin: "0000"
|
* Pin: "0000"
|
||||||
* Pic: "000000"
|
* Pic: "000000"
|
||||||
|
|
||||||
|
Admin characters have "hide" mode enabled by default. This means your character will be translucent on your screen, and completely invisible to others.
|
||||||
|
It will also prevent you from controlling mobs (making them stand still). To toggle this mode on and off, type "@hide" in the in-game chat.
|
||||||
|
|
||||||
By default, the server source is set to allow AUTO-REGISTERING. This means that, by simply typing in a "Login ID" and a "Password", you're able to create a new account.
|
By default, the server source is set to allow AUTO-REGISTERING. This means that, by simply typing in a "Login ID" and a "Password", you're able to create a new account.
|
||||||
|
|
||||||
After creating a character, experiment typing in all-chat "@commands".
|
After creating a character, experiment typing in all-chat "@commands".
|
||||||
|
|||||||
7
pom.xml
7
pom.xml
@@ -32,6 +32,7 @@
|
|||||||
<jcip-annotations.version>1.0</jcip-annotations.version> <!-- Annotations for concurrency documentation -->
|
<jcip-annotations.version>1.0</jcip-annotations.version> <!-- Annotations for concurrency documentation -->
|
||||||
<HikariCP.version>5.0.1</HikariCP.version> <!-- Database connection pool -->
|
<HikariCP.version>5.0.1</HikariCP.version> <!-- Database connection pool -->
|
||||||
<mysql-connector-java.version>8.0.30</mysql-connector-java.version> <!-- MySQL JDBC driver -->
|
<mysql-connector-java.version>8.0.30</mysql-connector-java.version> <!-- MySQL JDBC driver -->
|
||||||
|
<jdbi-version>3.35.0</jdbi-version> <!-- Convenience wrapper around JDBC -->
|
||||||
<junit.version>5.9.0</junit.version> <!-- Unit test -->
|
<junit.version>5.9.0</junit.version> <!-- Unit test -->
|
||||||
<mockito.version>4.7.0</mockito.version> <!-- Unit test -->
|
<mockito.version>4.7.0</mockito.version> <!-- Unit test -->
|
||||||
</properties>
|
</properties>
|
||||||
@@ -59,6 +60,12 @@
|
|||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>${mysql-connector-java.version}</version>
|
<version>${mysql-connector-java.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jdbi</groupId>
|
||||||
|
<artifactId>jdbi3-core</artifactId>
|
||||||
|
<version>${jdbi-version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- Networking -->
|
<!-- Networking -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ import static java.util.concurrent.TimeUnit.*;
|
|||||||
|
|
||||||
public class Character extends AbstractCharacterObject {
|
public class Character extends AbstractCharacterObject {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Character.class);
|
private static final Logger log = LoggerFactory.getLogger(Character.class);
|
||||||
private static final ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
|
||||||
private static final String LEVEL_200 = "[Congrats] %s has reached Level %d! Congratulate %s on such an amazing achievement!";
|
private static final String LEVEL_200 = "[Congrats] %s has reached Level %d! Congratulate %s on such an amazing achievement!";
|
||||||
private static final String[] BLOCKED_NAMES = {"admin", "owner", "moderator", "intern", "donor", "administrator", "FREDRICK", "help", "helper", "alert", "notice", "maplestory", "fuck", "wizet", "fucking", "negro", "fuk", "fuc", "penis", "pussy", "asshole", "gay",
|
private static final String[] BLOCKED_NAMES = {"admin", "owner", "moderator", "intern", "donor", "administrator", "FREDRICK", "help", "helper", "alert", "notice", "maplestory", "fuck", "wizet", "fucking", "negro", "fuk", "fuc", "penis", "pussy", "asshole", "gay",
|
||||||
"nigger", "homo", "suck", "cum", "shit", "shitty", "condom", "security", "official", "rape", "nigga", "sex", "tit", "boner", "orgy", "clit", "asshole", "fatass", "bitch", "support", "gamemaster", "cock", "gaay", "gm",
|
"nigger", "homo", "suck", "cum", "shit", "shitty", "condom", "security", "official", "rape", "nigga", "sex", "tit", "boner", "orgy", "clit", "asshole", "fatass", "bitch", "support", "gamemaster", "cock", "gaay", "gm",
|
||||||
@@ -717,7 +716,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
int maxbasedamage;
|
int maxbasedamage;
|
||||||
Item weapon_item = getInventory(InventoryType.EQUIPPED).getItem((short) -11);
|
Item weapon_item = getInventory(InventoryType.EQUIPPED).getItem((short) -11);
|
||||||
if (weapon_item != null) {
|
if (weapon_item != null) {
|
||||||
maxbasedamage = calculateMaxBaseDamage(watk, ii.getWeaponType(weapon_item.getItemId()));
|
maxbasedamage = calculateMaxBaseDamage(watk, ItemInformationProvider.getInstance().getWeaponType(weapon_item.getItemId()));
|
||||||
} else {
|
} else {
|
||||||
if (job.isA(Job.PIRATE) || job.isA(Job.THUNDERBREAKER1)) {
|
if (job.isA(Job.PIRATE) || job.isA(Job.THUNDERBREAKER1)) {
|
||||||
double weapMulti = 3;
|
double weapMulti = 3;
|
||||||
@@ -817,7 +816,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
String medal = "";
|
String medal = "";
|
||||||
final Item medalItem = getInventory(InventoryType.EQUIPPED).getItem((short) -49);
|
final Item medalItem = getInventory(InventoryType.EQUIPPED).getItem((short) -49);
|
||||||
if (medalItem != null) {
|
if (medalItem != null) {
|
||||||
medal = "<" + ii.getName(medalItem.getItemId()) + "> ";
|
medal = "<" + ItemInformationProvider.getInstance().getName(medalItem.getItemId()) + "> ";
|
||||||
}
|
}
|
||||||
return medal;
|
return medal;
|
||||||
}
|
}
|
||||||
@@ -1873,6 +1872,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
|
|
||||||
public boolean applyConsumeOnPickup(final int itemId) {
|
public boolean applyConsumeOnPickup(final int itemId) {
|
||||||
if (itemId / 1000000 == 2) {
|
if (itemId / 1000000 == 2) {
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
if (ii.isConsumeOnPickup(itemId)) {
|
if (ii.isConsumeOnPickup(itemId)) {
|
||||||
if (ItemConstants.isPartyItem(itemId)) {
|
if (ItemConstants.isPartyItem(itemId)) {
|
||||||
List<Character> partyMembers = this.getPartyMembersOnSameMap();
|
List<Character> partyMembers = this.getPartyMembersOnSameMap();
|
||||||
@@ -1943,6 +1943,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
|
|
||||||
Item mItem = mapitem.getItem();
|
Item mItem = mapitem.getItem();
|
||||||
boolean hasSpaceInventory = true;
|
boolean hasSpaceInventory = true;
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
if (ItemId.isNxCard(mapitem.getItemId()) || mapitem.getMeso() > 0 || ii.isConsumeOnPickup(mapitem.getItemId()) || (hasSpaceInventory = InventoryManipulator.checkSpace(client, mapitem.getItemId(), mItem.getQuantity(), mItem.getOwner()))) {
|
if (ItemId.isNxCard(mapitem.getItemId()) || mapitem.getMeso() > 0 || ii.isConsumeOnPickup(mapitem.getItemId()) || (hasSpaceInventory = InventoryManipulator.checkSpace(client, mapitem.getItemId(), mItem.getQuantity(), mItem.getOwner()))) {
|
||||||
int mapId = this.getMapId();
|
int mapId = this.getMapId();
|
||||||
|
|
||||||
@@ -2061,6 +2062,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canHoldUniques(List<Integer> itemids) {
|
public boolean canHoldUniques(List<Integer> itemids) {
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
for (Integer itemid : itemids) {
|
for (Integer itemid : itemids) {
|
||||||
if (ii.isPickupRestricted(itemid) && this.haveItem(itemid)) {
|
if (ii.isPickupRestricted(itemid) && this.haveItem(itemid)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -3740,6 +3742,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void cancelEffect(int itemId) {
|
public void cancelEffect(int itemId) {
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
cancelEffect(ii.getItemEffect(itemId), false, -1);
|
cancelEffect(ii.getItemEffect(itemId), false, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6793,6 +6796,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
StatEffect mse = ii.getItemEffect(couponid);
|
StatEffect mse = ii.getItemEffect(couponid);
|
||||||
mse.applyTo(this);
|
mse.applyTo(this);
|
||||||
}
|
}
|
||||||
@@ -7825,6 +7829,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
if (job.isA(Job.THIEF) || job.isA(Job.BOWMAN) || job.isA(Job.PIRATE) || job.isA(Job.NIGHTWALKER1) || job.isA(Job.WINDARCHER1)) {
|
if (job.isA(Job.THIEF) || job.isA(Job.BOWMAN) || job.isA(Job.PIRATE) || job.isA(Job.NIGHTWALKER1) || job.isA(Job.WINDARCHER1)) {
|
||||||
Item weapon_item = getInventory(InventoryType.EQUIPPED).getItem((short) -11);
|
Item weapon_item = getInventory(InventoryType.EQUIPPED).getItem((short) -11);
|
||||||
if (weapon_item != null) {
|
if (weapon_item != null) {
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
WeaponType weapon = ii.getWeaponType(weapon_item.getItemId());
|
WeaponType weapon = ii.getWeaponType(weapon_item.getItemId());
|
||||||
boolean bow = weapon == WeaponType.BOW;
|
boolean bow = weapon == WeaponType.BOW;
|
||||||
boolean crossbow = weapon == WeaponType.CROSSBOW;
|
boolean crossbow = weapon == WeaponType.CROSSBOW;
|
||||||
@@ -8792,22 +8797,6 @@ public class Character extends AbstractCharacterObject {
|
|||||||
return skillMacros;
|
return skillMacros;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendNote(String to, String msg, byte fame) throws SQLException {
|
|
||||||
sendNote(to, this.getName(), msg, fame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void sendNote(String to, String from, String msg, byte fame) throws SQLException {
|
|
||||||
try (Connection con = DatabaseConnection.getConnection();
|
|
||||||
PreparedStatement ps = con.prepareStatement("INSERT INTO notes (`to`, `from`, `message`, `timestamp`, `fame`) VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) {
|
|
||||||
ps.setString(1, to);
|
|
||||||
ps.setString(2, from);
|
|
||||||
ps.setString(3, msg);
|
|
||||||
ps.setLong(4, Server.getInstance().getCurrentTime());
|
|
||||||
ps.setByte(5, fame);
|
|
||||||
ps.executeUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setAriantRoomLeader(int room, String charname) {
|
public static void setAriantRoomLeader(int room, String charname) {
|
||||||
ariantroomleader[room] = charname;
|
ariantroomleader[room] = charname;
|
||||||
}
|
}
|
||||||
@@ -9219,20 +9208,6 @@ public class Character extends AbstractCharacterObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changeName(String name) {
|
|
||||||
FredrickProcessor.removeFredrickReminders(this.getId());
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
try (Connection con = DatabaseConnection.getConnection();
|
|
||||||
PreparedStatement ps = con.prepareStatement("UPDATE `characters` SET `name` = ? WHERE `id` = ?")) {
|
|
||||||
ps.setString(1, name);
|
|
||||||
ps.setInt(2, id);
|
|
||||||
ps.executeUpdate();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDoorSlot() {
|
public int getDoorSlot() {
|
||||||
if (doorSlot != -1) {
|
if (doorSlot != -1) {
|
||||||
return doorSlot;
|
return doorSlot;
|
||||||
@@ -9331,6 +9306,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
return (sellAllItemsFromPosition(ii, type, it.getPosition()));
|
return (sellAllItemsFromPosition(ii, type, it.getPosition()));
|
||||||
} finally {
|
} finally {
|
||||||
inv.unlockInventory();
|
inv.unlockInventory();
|
||||||
@@ -9413,6 +9389,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
private List<Equip> getUpgradeableEquipped() {
|
private List<Equip> getUpgradeableEquipped() {
|
||||||
List<Equip> list = new LinkedList<>();
|
List<Equip> list = new LinkedList<>();
|
||||||
|
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
for (Item item : getInventory(InventoryType.EQUIPPED)) {
|
for (Item item : getInventory(InventoryType.EQUIPPED)) {
|
||||||
if (ii.isUpgradeable(item.getItemId())) {
|
if (ii.isUpgradeable(item.getItemId())) {
|
||||||
list.add((Equip) item);
|
list.add((Equip) item);
|
||||||
@@ -9525,6 +9502,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
|
|
||||||
private void standaloneMerge(Map<StatUpgrade, Float> statups, Client c, InventoryType type, short slot, Item item) {
|
private void standaloneMerge(Map<StatUpgrade, Float> statups, Client c, InventoryType type, short slot, Item item) {
|
||||||
short quantity;
|
short quantity;
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
if (item == null || (quantity = item.getQuantity()) < 1 || ii.isCash(item.getItemId()) || !ii.isUpgradeable(item.getItemId()) || hasMergeFlag(item)) {
|
if (item == null || (quantity = item.getQuantity()) < 1 || ii.isCash(item.getItemId()) || !ii.isUpgradeable(item.getItemId()) || hasMergeFlag(item)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -9619,7 +9597,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
String medal = "";
|
String medal = "";
|
||||||
Item medalItem = mapOwner.getInventory(InventoryType.EQUIPPED).getItem((short) -49);
|
Item medalItem = mapOwner.getInventory(InventoryType.EQUIPPED).getItem((short) -49);
|
||||||
if (medalItem != null) {
|
if (medalItem != null) {
|
||||||
medal = "<" + ii.getName(medalItem.getItemId()) + "> ";
|
medal = "<" + ItemInformationProvider.getInstance().getName(medalItem.getItemId()) + "> ";
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> strLines = new LinkedList<>();
|
List<String> strLines = new LinkedList<>();
|
||||||
@@ -9640,21 +9618,6 @@ public class Character extends AbstractCharacterObject {
|
|||||||
client.announceHint(msg, length);
|
client.announceHint(msg, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showNote() {
|
|
||||||
try (Connection con = DatabaseConnection.getConnection();
|
|
||||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM notes WHERE `to` = ? AND `deleted` = 0", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
|
|
||||||
ps.setString(1, this.getName());
|
|
||||||
try (ResultSet rs = ps.executeQuery()) {
|
|
||||||
rs.last();
|
|
||||||
int count = rs.getRow();
|
|
||||||
rs.first();
|
|
||||||
sendPacket(PacketCreator.showNotes(rs, count));
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void silentGiveBuffs(List<Pair<Long, PlayerBuffValueHolder>> buffs) {
|
public void silentGiveBuffs(List<Pair<Long, PlayerBuffValueHolder>> buffs) {
|
||||||
for (Pair<Long, PlayerBuffValueHolder> mbsv : buffs) {
|
for (Pair<Long, PlayerBuffValueHolder> mbsv : buffs) {
|
||||||
PlayerBuffValueHolder mbsvh = mbsv.getRight();
|
PlayerBuffValueHolder mbsvh = mbsv.getRight();
|
||||||
@@ -10357,6 +10320,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Collection<Item> eqpList = new LinkedHashSet<>();
|
Collection<Item> eqpList = new LinkedHashSet<>();
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
for (Item it : fullList) {
|
for (Item it : fullList) {
|
||||||
if (!ii.isCash(it.getItemId())) {
|
if (!ii.isCash(it.getItemId())) {
|
||||||
eqpList.add(it);
|
eqpList.add(it);
|
||||||
@@ -10372,6 +10336,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
expGain = Integer.MAX_VALUE;
|
expGain = Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
for (Item item : getUpgradeableEquipList()) {
|
for (Item item : getUpgradeableEquipList()) {
|
||||||
Equip nEquip = (Equip) item;
|
Equip nEquip = (Equip) item;
|
||||||
String itemName = ii.getName(nEquip.getItemId());
|
String itemName = ii.getName(nEquip.getItemId());
|
||||||
@@ -10387,6 +10352,7 @@ public class Character extends AbstractCharacterObject {
|
|||||||
public void showAllEquipFeatures() {
|
public void showAllEquipFeatures() {
|
||||||
String showMsg = "";
|
String showMsg = "";
|
||||||
|
|
||||||
|
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||||
for (Item item : getInventory(InventoryType.EQUIPPED).list()) {
|
for (Item item : getInventory(InventoryType.EQUIPPED).list()) {
|
||||||
Equip nEquip = (Equip) item;
|
Equip nEquip = (Equip) item;
|
||||||
String itemName = ii.getName(nEquip.getItemId());
|
String itemName = ii.getName(nEquip.getItemId());
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import server.ItemInformationProvider;
|
import server.ItemInformationProvider;
|
||||||
import server.maps.HiredMerchant;
|
import server.maps.HiredMerchant;
|
||||||
|
import service.NoteService;
|
||||||
import tools.DatabaseConnection;
|
import tools.DatabaseConnection;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -54,6 +54,12 @@ public class FredrickProcessor {
|
|||||||
private static final Logger log = LoggerFactory.getLogger(FredrickProcessor.class);
|
private static final Logger log = LoggerFactory.getLogger(FredrickProcessor.class);
|
||||||
private static final int[] dailyReminders = new int[]{2, 5, 10, 15, 30, 60, 90, Integer.MAX_VALUE};
|
private static final int[] dailyReminders = new int[]{2, 5, 10, 15, 30, 60, 90, Integer.MAX_VALUE};
|
||||||
|
|
||||||
|
private final NoteService noteService;
|
||||||
|
|
||||||
|
public FredrickProcessor(NoteService noteService) {
|
||||||
|
this.noteService = noteService;
|
||||||
|
}
|
||||||
|
|
||||||
private static byte canRetrieveFromFredrick(Character chr, List<Pair<Item, InventoryType>> items) {
|
private static byte canRetrieveFromFredrick(Character chr, List<Pair<Item, InventoryType>> items) {
|
||||||
if (!Inventory.checkSpotsAndOwnership(chr, items)) {
|
if (!Inventory.checkSpotsAndOwnership(chr, items)) {
|
||||||
List<Integer> itemids = new LinkedList<>();
|
List<Integer> itemids = new LinkedList<>();
|
||||||
@@ -127,10 +133,6 @@ public class FredrickProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeFredrickReminders(int cid) {
|
|
||||||
removeFredrickReminders(Collections.singletonList(new Pair<>(cid, 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void removeFredrickReminders(List<Pair<Integer, Integer>> expiredCids) {
|
private static void removeFredrickReminders(List<Pair<Integer, Integer>> expiredCids) {
|
||||||
List<String> expiredCnames = new LinkedList<>();
|
List<String> expiredCnames = new LinkedList<>();
|
||||||
for (Pair<Integer, Integer> id : expiredCids) {
|
for (Pair<Integer, Integer> id : expiredCids) {
|
||||||
@@ -153,7 +155,7 @@ public class FredrickProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void runFredrickSchedule() {
|
public void runFredrickSchedule() {
|
||||||
try (Connection con = DatabaseConnection.getConnection()) {
|
try (Connection con = DatabaseConnection.getConnection()) {
|
||||||
List<Pair<Integer, Integer>> expiredCids = new LinkedList<>();
|
List<Pair<Integer, Integer>> expiredCids = new LinkedList<>();
|
||||||
List<Pair<Pair<Integer, String>, Integer>> notifCids = new LinkedList<>();
|
List<Pair<Pair<Integer, String>, Integer>> notifCids = new LinkedList<>();
|
||||||
@@ -241,7 +243,7 @@ public class FredrickProcessor {
|
|||||||
ps.addBatch();
|
ps.addBatch();
|
||||||
|
|
||||||
String msg = fredrickReminderMessage(cid.getRight() - 1);
|
String msg = fredrickReminderMessage(cid.getRight() - 1);
|
||||||
Character.sendNote(cid.getLeft().getRight(), "FREDRICK", msg, (byte) 0);
|
noteService.sendNormal(msg, "FREDRICK", cid.getLeft().getRight());
|
||||||
}
|
}
|
||||||
|
|
||||||
ps.executeBatch();
|
ps.executeBatch();
|
||||||
@@ -266,7 +268,7 @@ public class FredrickProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void fredrickRetrieveItems(Client c) { // thanks Gustav for pointing out the dupe on Fredrick handling
|
public void fredrickRetrieveItems(Client c) { // thanks Gustav for pointing out the dupe on Fredrick handling
|
||||||
if (c.tryacquireClient()) {
|
if (c.tryacquireClient()) {
|
||||||
try {
|
try {
|
||||||
Character chr = c.getPlayer();
|
Character chr = c.getPlayer();
|
||||||
|
|||||||
10
src/main/java/database/DaoException.java
Normal file
10
src/main/java/database/DaoException.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package database;
|
||||||
|
|
||||||
|
import org.jdbi.v3.core.JdbiException;
|
||||||
|
|
||||||
|
public class DaoException extends JdbiException {
|
||||||
|
|
||||||
|
public DaoException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/main/java/database/note/NoteDao.java
Normal file
89
src/main/java/database/note/NoteDao.java
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package database.note;
|
||||||
|
|
||||||
|
import database.DaoException;
|
||||||
|
import model.Note;
|
||||||
|
import org.jdbi.v3.core.Handle;
|
||||||
|
import org.jdbi.v3.core.JdbiException;
|
||||||
|
import tools.DatabaseConnection;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class NoteDao {
|
||||||
|
|
||||||
|
public void save(Note note) {
|
||||||
|
try (Handle handle = DatabaseConnection.getHandle()) {
|
||||||
|
handle.createUpdate("""
|
||||||
|
INSERT INTO notes (`message`, `from`, `to`, `timestamp`, `fame`, `deleted`)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)""")
|
||||||
|
.bind(0, note.message())
|
||||||
|
.bind(1, note.from())
|
||||||
|
.bind(2, note.to())
|
||||||
|
.bind(3, note.timestamp())
|
||||||
|
.bind(4, note.fame())
|
||||||
|
.bind(5, 0)
|
||||||
|
.execute();
|
||||||
|
} catch (JdbiException e) {
|
||||||
|
throw new DaoException("Failed to save note: %s".formatted(note.toString()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Note> findAllByTo(String to) {
|
||||||
|
try (Handle handle = DatabaseConnection.getHandle()) {
|
||||||
|
return handle.createQuery("""
|
||||||
|
SELECT *
|
||||||
|
FROM notes
|
||||||
|
WHERE `deleted` = 0
|
||||||
|
AND `to` = ?""")
|
||||||
|
.bind(0, to)
|
||||||
|
.mapTo(Note.class)
|
||||||
|
.list();
|
||||||
|
} catch (JdbiException e) {
|
||||||
|
throw new DaoException("Failed to find notes sent to: %s".formatted(to), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Note> delete(int id) {
|
||||||
|
try (Handle handle = DatabaseConnection.getHandle()) {
|
||||||
|
Optional<Note> note = findById(handle, id);
|
||||||
|
if (note.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
deleteById(handle, id);
|
||||||
|
|
||||||
|
return note;
|
||||||
|
} catch (JdbiException e) {
|
||||||
|
throw new DaoException("Failed to delete note with id: %d".formatted(id), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Note> findById(Handle handle, int id) {
|
||||||
|
final Optional<Note> note;
|
||||||
|
try {
|
||||||
|
note = handle.createQuery("""
|
||||||
|
SELECT *
|
||||||
|
FROM notes
|
||||||
|
WHERE `deleted` = 0
|
||||||
|
AND `id` = ?""")
|
||||||
|
.bind(0, id)
|
||||||
|
.mapTo(Note.class)
|
||||||
|
.findOne();
|
||||||
|
} catch (JdbiException e) {
|
||||||
|
throw new DaoException("Failed find note with id %s".formatted(id), e);
|
||||||
|
}
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteById(Handle handle, int id) {
|
||||||
|
try {
|
||||||
|
handle.createUpdate("""
|
||||||
|
UPDATE notes
|
||||||
|
SET `deleted` = 1
|
||||||
|
WHERE `id` = ?""")
|
||||||
|
.bind(0, id)
|
||||||
|
.execute();
|
||||||
|
} catch (JdbiException e) {
|
||||||
|
throw new DaoException("Failed to delete note with id %d".formatted(id), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/main/java/database/note/NoteRowMapper.java
Normal file
22
src/main/java/database/note/NoteRowMapper.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package database.note;
|
||||||
|
|
||||||
|
import model.Note;
|
||||||
|
import org.jdbi.v3.core.mapper.RowMapper;
|
||||||
|
import org.jdbi.v3.core.statement.StatementContext;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
public class NoteRowMapper implements RowMapper<Note> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Note map(ResultSet rs, StatementContext ctx) throws SQLException {
|
||||||
|
int id = rs.getInt("id");
|
||||||
|
String message = rs.getString("message");
|
||||||
|
String from = rs.getString("from");
|
||||||
|
String to = rs.getString("to");
|
||||||
|
long timestamp = rs.getLong("timestamp");
|
||||||
|
int fame = rs.getInt("fame");
|
||||||
|
return new Note(id, message, from, to, timestamp, fame);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/main/java/model/Note.java
Normal file
21
src/main/java/model/Note.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record Note(int id, String message, String from, String to, long timestamp, int fame) {
|
||||||
|
private static final int PLACEHOLDER_ID = -1;
|
||||||
|
|
||||||
|
public Note {
|
||||||
|
Objects.requireNonNull(message);
|
||||||
|
Objects.requireNonNull(from);
|
||||||
|
Objects.requireNonNull(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Note createNormal(String message, String from, String to, long timestamp) {
|
||||||
|
return new Note(PLACEHOLDER_ID, message, from, to, timestamp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Note createGift(String message, String from, String to, long timestamp) {
|
||||||
|
return new Note(PLACEHOLDER_ID, message, from, to, timestamp, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/main/java/net/ChannelDependencies.java
Normal file
14
src/main/java/net/ChannelDependencies.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package net;
|
||||||
|
|
||||||
|
import client.processor.npc.FredrickProcessor;
|
||||||
|
import service.NoteService;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record ChannelDependencies(NoteService noteService, FredrickProcessor fredrickProcessor) {
|
||||||
|
|
||||||
|
public ChannelDependencies {
|
||||||
|
Objects.requireNonNull(noteService);
|
||||||
|
Objects.requireNonNull(fredrickProcessor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,9 @@ import java.util.Map;
|
|||||||
public final class PacketProcessor {
|
public final class PacketProcessor {
|
||||||
private static final Logger log = LoggerFactory.getLogger(PacketProcessor.class);
|
private static final Logger log = LoggerFactory.getLogger(PacketProcessor.class);
|
||||||
private static final Map<String, PacketProcessor> instances = new LinkedHashMap<>();
|
private static final Map<String, PacketProcessor> instances = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private static ChannelDependencies channelDeps;
|
||||||
|
|
||||||
private PacketHandler[] handlers;
|
private PacketHandler[] handlers;
|
||||||
|
|
||||||
private PacketProcessor() {
|
private PacketProcessor() {
|
||||||
@@ -49,11 +52,19 @@ public final class PacketProcessor {
|
|||||||
handlers = new PacketHandler[maxRecvOp + 1];
|
handlers = new PacketHandler[maxRecvOp + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void registerGameHandlerDependencies(ChannelDependencies channelDependencies) {
|
||||||
|
PacketProcessor.channelDeps = channelDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
public static PacketProcessor getLoginServerProcessor() {
|
public static PacketProcessor getLoginServerProcessor() {
|
||||||
return getProcessor(LoginServer.WORLD_ID, LoginServer.CHANNEL_ID);
|
return getProcessor(LoginServer.WORLD_ID, LoginServer.CHANNEL_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PacketProcessor getChannelServerProcessor(int world, int channel) {
|
public static PacketProcessor getChannelServerProcessor(int world, int channel) {
|
||||||
|
if (channelDeps == null) {
|
||||||
|
throw new IllegalStateException("Unable to get channel server processor - dependencies are not registered");
|
||||||
|
}
|
||||||
|
|
||||||
return getProcessor(world, channel);
|
return getProcessor(world, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +152,7 @@ public final class PacketProcessor {
|
|||||||
registerHandler(RecvOpcode.ITEM_SORT, new InventoryMergeHandler());
|
registerHandler(RecvOpcode.ITEM_SORT, new InventoryMergeHandler());
|
||||||
registerHandler(RecvOpcode.ITEM_MOVE, new ItemMoveHandler());
|
registerHandler(RecvOpcode.ITEM_MOVE, new ItemMoveHandler());
|
||||||
registerHandler(RecvOpcode.MESO_DROP, new MesoDropHandler());
|
registerHandler(RecvOpcode.MESO_DROP, new MesoDropHandler());
|
||||||
registerHandler(RecvOpcode.PLAYER_LOGGEDIN, new PlayerLoggedinHandler());
|
registerHandler(RecvOpcode.PLAYER_LOGGEDIN, new PlayerLoggedinHandler(channelDeps.noteService()));
|
||||||
registerHandler(RecvOpcode.CHANGE_MAP, new ChangeMapHandler());
|
registerHandler(RecvOpcode.CHANGE_MAP, new ChangeMapHandler());
|
||||||
registerHandler(RecvOpcode.MOVE_LIFE, new MoveLifeHandler());
|
registerHandler(RecvOpcode.MOVE_LIFE, new MoveLifeHandler());
|
||||||
registerHandler(RecvOpcode.CLOSE_RANGE_ATTACK, new CloseRangeDamageHandler());
|
registerHandler(RecvOpcode.CLOSE_RANGE_ATTACK, new CloseRangeDamageHandler());
|
||||||
@@ -149,7 +160,7 @@ public final class PacketProcessor {
|
|||||||
registerHandler(RecvOpcode.MAGIC_ATTACK, new MagicDamageHandler());
|
registerHandler(RecvOpcode.MAGIC_ATTACK, new MagicDamageHandler());
|
||||||
registerHandler(RecvOpcode.TAKE_DAMAGE, new TakeDamageHandler());
|
registerHandler(RecvOpcode.TAKE_DAMAGE, new TakeDamageHandler());
|
||||||
registerHandler(RecvOpcode.MOVE_PLAYER, new MovePlayerHandler());
|
registerHandler(RecvOpcode.MOVE_PLAYER, new MovePlayerHandler());
|
||||||
registerHandler(RecvOpcode.USE_CASH_ITEM, new UseCashItemHandler());
|
registerHandler(RecvOpcode.USE_CASH_ITEM, new UseCashItemHandler(channelDeps.noteService()));
|
||||||
registerHandler(RecvOpcode.USE_ITEM, new UseItemHandler());
|
registerHandler(RecvOpcode.USE_ITEM, new UseItemHandler());
|
||||||
registerHandler(RecvOpcode.USE_RETURN_SCROLL, new UseItemHandler());
|
registerHandler(RecvOpcode.USE_RETURN_SCROLL, new UseItemHandler());
|
||||||
registerHandler(RecvOpcode.USE_UPGRADE_SCROLL, new ScrollHandler());
|
registerHandler(RecvOpcode.USE_UPGRADE_SCROLL, new ScrollHandler());
|
||||||
@@ -191,7 +202,7 @@ public final class PacketProcessor {
|
|||||||
registerHandler(RecvOpcode.MESSENGER, new MessengerHandler());
|
registerHandler(RecvOpcode.MESSENGER, new MessengerHandler());
|
||||||
registerHandler(RecvOpcode.NPC_ACTION, new NPCAnimationHandler());
|
registerHandler(RecvOpcode.NPC_ACTION, new NPCAnimationHandler());
|
||||||
registerHandler(RecvOpcode.CHECK_CASH, new TouchingCashShopHandler());
|
registerHandler(RecvOpcode.CHECK_CASH, new TouchingCashShopHandler());
|
||||||
registerHandler(RecvOpcode.CASHSHOP_OPERATION, new CashOperationHandler());
|
registerHandler(RecvOpcode.CASHSHOP_OPERATION, new CashOperationHandler(channelDeps.noteService()));
|
||||||
registerHandler(RecvOpcode.COUPON_CODE, new CouponCodeHandler());
|
registerHandler(RecvOpcode.COUPON_CODE, new CouponCodeHandler());
|
||||||
registerHandler(RecvOpcode.SPAWN_PET, new SpawnPetHandler());
|
registerHandler(RecvOpcode.SPAWN_PET, new SpawnPetHandler());
|
||||||
registerHandler(RecvOpcode.MOVE_PET, new MovePetHandler());
|
registerHandler(RecvOpcode.MOVE_PET, new MovePetHandler());
|
||||||
@@ -204,11 +215,11 @@ public final class PacketProcessor {
|
|||||||
registerHandler(RecvOpcode.CANCEL_DEBUFF, new CancelDebuffHandler());
|
registerHandler(RecvOpcode.CANCEL_DEBUFF, new CancelDebuffHandler());
|
||||||
registerHandler(RecvOpcode.USE_SKILL_BOOK, new SkillBookHandler());
|
registerHandler(RecvOpcode.USE_SKILL_BOOK, new SkillBookHandler());
|
||||||
registerHandler(RecvOpcode.SKILL_MACRO, new SkillMacroHandler());
|
registerHandler(RecvOpcode.SKILL_MACRO, new SkillMacroHandler());
|
||||||
registerHandler(RecvOpcode.NOTE_ACTION, new NoteActionHandler());
|
registerHandler(RecvOpcode.NOTE_ACTION, new NoteActionHandler(channelDeps.noteService()));
|
||||||
registerHandler(RecvOpcode.CLOSE_CHALKBOARD, new CloseChalkboardHandler());
|
registerHandler(RecvOpcode.CLOSE_CHALKBOARD, new CloseChalkboardHandler());
|
||||||
registerHandler(RecvOpcode.USE_MOUNT_FOOD, new UseMountFoodHandler());
|
registerHandler(RecvOpcode.USE_MOUNT_FOOD, new UseMountFoodHandler());
|
||||||
registerHandler(RecvOpcode.MTS_OPERATION, new MTSHandler());
|
registerHandler(RecvOpcode.MTS_OPERATION, new MTSHandler());
|
||||||
registerHandler(RecvOpcode.RING_ACTION, new RingActionHandler());
|
registerHandler(RecvOpcode.RING_ACTION, new RingActionHandler(channelDeps.noteService()));
|
||||||
registerHandler(RecvOpcode.SPOUSE_CHAT, new SpouseChatHandler());
|
registerHandler(RecvOpcode.SPOUSE_CHAT, new SpouseChatHandler());
|
||||||
registerHandler(RecvOpcode.PET_AUTO_POT, new PetAutoPotHandler());
|
registerHandler(RecvOpcode.PET_AUTO_POT, new PetAutoPotHandler());
|
||||||
registerHandler(RecvOpcode.PET_EXCLUDE_ITEMS, new PetExcludeItemsHandler());
|
registerHandler(RecvOpcode.PET_EXCLUDE_ITEMS, new PetExcludeItemsHandler());
|
||||||
@@ -262,7 +273,7 @@ public final class PacketProcessor {
|
|||||||
registerHandler(RecvOpcode.COCONUT, new CoconutHandler());
|
registerHandler(RecvOpcode.COCONUT, new CoconutHandler());
|
||||||
registerHandler(RecvOpcode.ARAN_COMBO_COUNTER, new AranComboHandler());
|
registerHandler(RecvOpcode.ARAN_COMBO_COUNTER, new AranComboHandler());
|
||||||
registerHandler(RecvOpcode.CLICK_GUIDE, new ClickGuideHandler());
|
registerHandler(RecvOpcode.CLICK_GUIDE, new ClickGuideHandler());
|
||||||
registerHandler(RecvOpcode.FREDRICK_ACTION, new FredrickHandler());
|
registerHandler(RecvOpcode.FREDRICK_ACTION, new FredrickHandler(channelDeps.fredrickProcessor()));
|
||||||
registerHandler(RecvOpcode.MONSTER_CARNIVAL, new MonsterCarnivalHandler());
|
registerHandler(RecvOpcode.MONSTER_CARNIVAL, new MonsterCarnivalHandler());
|
||||||
registerHandler(RecvOpcode.REMOTE_STORE, new RemoteStoreHandler());
|
registerHandler(RecvOpcode.REMOTE_STORE, new RemoteStoreHandler());
|
||||||
registerHandler(RecvOpcode.WEDDING_ACTION, new WeddingHandler());
|
registerHandler(RecvOpcode.WEDDING_ACTION, new WeddingHandler());
|
||||||
|
|||||||
13
src/main/java/net/packet/out/SendNoteSuccessPacket.java
Normal file
13
src/main/java/net/packet/out/SendNoteSuccessPacket.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package net.packet.out;
|
||||||
|
|
||||||
|
import net.opcodes.SendOpcode;
|
||||||
|
import net.packet.ByteBufOutPacket;
|
||||||
|
|
||||||
|
public final class SendNoteSuccessPacket extends ByteBufOutPacket {
|
||||||
|
|
||||||
|
public SendNoteSuccessPacket() {
|
||||||
|
super(SendOpcode.MEMO_RESULT);
|
||||||
|
|
||||||
|
writeByte(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/main/java/net/packet/out/ShowNotesPacket.java
Normal file
30
src/main/java/net/packet/out/ShowNotesPacket.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package net.packet.out;
|
||||||
|
|
||||||
|
import model.Note;
|
||||||
|
import net.opcodes.SendOpcode;
|
||||||
|
import net.packet.ByteBufOutPacket;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static tools.PacketCreator.getTime;
|
||||||
|
|
||||||
|
public final class ShowNotesPacket extends ByteBufOutPacket {
|
||||||
|
|
||||||
|
public ShowNotesPacket(List<Note> notes) {
|
||||||
|
super(SendOpcode.MEMO_RESULT);
|
||||||
|
Objects.requireNonNull(notes);
|
||||||
|
|
||||||
|
writeByte(3);
|
||||||
|
writeByte(notes.size());
|
||||||
|
notes.forEach(this::writeNote);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeNote(Note note) {
|
||||||
|
writeInt(note.id());
|
||||||
|
writeString(note.from() + " "); //Stupid nexon forgot space lol
|
||||||
|
writeString(note.message());
|
||||||
|
writeLong(getTime(note.timestamp()));
|
||||||
|
writeByte(note.fame());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,11 +30,15 @@ import client.inventory.Item;
|
|||||||
import client.inventory.ItemFactory;
|
import client.inventory.ItemFactory;
|
||||||
import client.inventory.manipulator.CashIdGenerator;
|
import client.inventory.manipulator.CashIdGenerator;
|
||||||
import client.newyear.NewYearCardRecord;
|
import client.newyear.NewYearCardRecord;
|
||||||
|
import client.processor.npc.FredrickProcessor;
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
import constants.game.GameConstants;
|
import constants.game.GameConstants;
|
||||||
import constants.inventory.ItemConstants;
|
import constants.inventory.ItemConstants;
|
||||||
import constants.net.OpcodeConstants;
|
import constants.net.OpcodeConstants;
|
||||||
import constants.net.ServerConstants;
|
import constants.net.ServerConstants;
|
||||||
|
import database.note.NoteDao;
|
||||||
|
import net.ChannelDependencies;
|
||||||
|
import net.PacketProcessor;
|
||||||
import net.netty.LoginServer;
|
import net.netty.LoginServer;
|
||||||
import net.packet.Packet;
|
import net.packet.Packet;
|
||||||
import net.server.channel.Channel;
|
import net.server.channel.Channel;
|
||||||
@@ -55,6 +59,7 @@ import server.TimerManager;
|
|||||||
import server.expeditions.ExpeditionBossLog;
|
import server.expeditions.ExpeditionBossLog;
|
||||||
import server.life.PlayerNPCFactory;
|
import server.life.PlayerNPCFactory;
|
||||||
import server.quest.Quest;
|
import server.quest.Quest;
|
||||||
|
import service.NoteService;
|
||||||
import tools.DatabaseConnection;
|
import tools.DatabaseConnection;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
|
|
||||||
@@ -91,6 +96,7 @@ public class Server {
|
|||||||
private static final Set<Integer> activeFly = new HashSet<>();
|
private static final Set<Integer> activeFly = new HashSet<>();
|
||||||
private static final Map<Integer, Integer> couponRates = new HashMap<>(30);
|
private static final Map<Integer, Integer> couponRates = new HashMap<>(30);
|
||||||
private static final List<Integer> activeCoupons = new LinkedList<>();
|
private static final List<Integer> activeCoupons = new LinkedList<>();
|
||||||
|
private static ChannelDependencies channelDependencies;
|
||||||
|
|
||||||
private LoginServer loginServer;
|
private LoginServer loginServer;
|
||||||
private final List<Map<Integer, String>> channels = new LinkedList<>();
|
private final List<Map<Integer, String>> channels = new LinkedList<>();
|
||||||
@@ -838,6 +844,8 @@ public class Server {
|
|||||||
throw new IllegalStateException("Failed to initiate a connection to the database");
|
throw new IllegalStateException("Failed to initiate a connection to the database");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channelDependencies = registerChannelDependencies();
|
||||||
|
|
||||||
final ExecutorService initExecutor = Executors.newFixedThreadPool(10);
|
final ExecutorService initExecutor = Executors.newFixedThreadPool(10);
|
||||||
// Run slow operations asynchronously to make startup faster
|
// Run slow operations asynchronously to make startup faster
|
||||||
final List<Future<?>> futures = new ArrayList<>();
|
final List<Future<?>> futures = new ArrayList<>();
|
||||||
@@ -866,7 +874,7 @@ public class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ThreadManager.getInstance().start();
|
ThreadManager.getInstance().start();
|
||||||
initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan
|
initializeTimelyTasks(channelDependencies); // aggregated method for timely tasks thanks to lxconan
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS);
|
int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS);
|
||||||
@@ -914,6 +922,16 @@ public class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ChannelDependencies registerChannelDependencies() {
|
||||||
|
NoteService noteService = new NoteService(new NoteDao());
|
||||||
|
FredrickProcessor fredrickProcessor = new FredrickProcessor(noteService);
|
||||||
|
ChannelDependencies channelDependencies = new ChannelDependencies(noteService, fredrickProcessor);
|
||||||
|
|
||||||
|
PacketProcessor.registerGameHandlerDependencies(channelDependencies);
|
||||||
|
|
||||||
|
return channelDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
private LoginServer initLoginServer(int port) {
|
private LoginServer initLoginServer(int port) {
|
||||||
LoginServer loginServer = new LoginServer(port);
|
LoginServer loginServer = new LoginServer(port);
|
||||||
loginServer.start();
|
loginServer.start();
|
||||||
@@ -932,7 +950,7 @@ public class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeTimelyTasks() {
|
private void initializeTimelyTasks(ChannelDependencies channelDependencies) {
|
||||||
TimerManager tMan = TimerManager.getInstance();
|
TimerManager tMan = TimerManager.getInstance();
|
||||||
tMan.start();
|
tMan.start();
|
||||||
tMan.register(tMan.purge(), YamlConfig.config.server.PURGING_INTERVAL);//Purging ftw...
|
tMan.register(tMan.purge(), YamlConfig.config.server.PURGING_INTERVAL);//Purging ftw...
|
||||||
@@ -946,7 +964,7 @@ public class Server {
|
|||||||
tMan.register(new LoginCoordinatorTask(), HOURS.toMillis(1), timeLeft);
|
tMan.register(new LoginCoordinatorTask(), HOURS.toMillis(1), timeLeft);
|
||||||
tMan.register(new EventRecallCoordinatorTask(), HOURS.toMillis(1), timeLeft);
|
tMan.register(new EventRecallCoordinatorTask(), HOURS.toMillis(1), timeLeft);
|
||||||
tMan.register(new LoginStorageTask(), MINUTES.toMillis(2), MINUTES.toMillis(2));
|
tMan.register(new LoginStorageTask(), MINUTES.toMillis(2), MINUTES.toMillis(2));
|
||||||
tMan.register(new DueyFredrickTask(), HOURS.toMillis(1), timeLeft);
|
tMan.register(new DueyFredrickTask(channelDependencies.fredrickProcessor()), HOURS.toMillis(1), timeLeft);
|
||||||
tMan.register(new InvitationTask(), SECONDS.toMillis(30), SECONDS.toMillis(30));
|
tMan.register(new InvitationTask(), SECONDS.toMillis(30), SECONDS.toMillis(30));
|
||||||
tMan.register(new RespawnTask(), YamlConfig.config.server.RESPAWN_INTERVAL, YamlConfig.config.server.RESPAWN_INTERVAL);
|
tMan.register(new RespawnTask(), YamlConfig.config.server.RESPAWN_INTERVAL, YamlConfig.config.server.RESPAWN_INTERVAL);
|
||||||
|
|
||||||
@@ -1164,7 +1182,7 @@ public class Server {
|
|||||||
public void expelMember(GuildCharacter initiator, String name, int cid) {
|
public void expelMember(GuildCharacter initiator, String name, int cid) {
|
||||||
Guild g = guilds.get(initiator.getGuildId());
|
Guild g = guilds.get(initiator.getGuildId());
|
||||||
if (g != null) {
|
if (g != null) {
|
||||||
g.expelMember(initiator, name, cid);
|
g.expelMember(initiator, name, cid, channelDependencies.noteService());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ import server.CashShop;
|
|||||||
import server.CashShop.CashItem;
|
import server.CashShop.CashItem;
|
||||||
import server.CashShop.CashItemFactory;
|
import server.CashShop.CashItemFactory;
|
||||||
import server.ItemInformationProvider;
|
import server.ItemInformationProvider;
|
||||||
|
import service.NoteService;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -54,6 +54,12 @@ import static java.util.concurrent.TimeUnit.DAYS;
|
|||||||
public final class CashOperationHandler extends AbstractPacketHandler {
|
public final class CashOperationHandler extends AbstractPacketHandler {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CashOperationHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(CashOperationHandler.class);
|
||||||
|
|
||||||
|
private final NoteService noteService;
|
||||||
|
|
||||||
|
public CashOperationHandler(NoteService noteService) {
|
||||||
|
this.noteService = noteService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePacket(InPacket p, Client c) {
|
public void handlePacket(InPacket p, Client c) {
|
||||||
Character chr = c.getPlayer();
|
Character chr = c.getPlayer();
|
||||||
@@ -128,14 +134,13 @@ public final class CashOperationHandler extends AbstractPacketHandler {
|
|||||||
cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN());
|
cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN());
|
||||||
c.sendPacket(PacketCreator.showGiftSucceed(recipient.get("name"), cItem));
|
c.sendPacket(PacketCreator.showGiftSucceed(recipient.get("name"), cItem));
|
||||||
c.sendPacket(PacketCreator.showCash(chr));
|
c.sendPacket(PacketCreator.showCash(chr));
|
||||||
try {
|
|
||||||
chr.sendNote(recipient.get("name"), chr.getName() + " has sent you a gift! Go check out the Cash Shop.", (byte) 0); //fame or not
|
String noteMessage = chr.getName() + " has sent you a gift! Go check out the Cash Shop.";
|
||||||
} catch (SQLException ex) {
|
noteService.sendNormal(noteMessage, chr.getName(), recipient.get("name"));
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
Character receiver = c.getChannelServer().getPlayerStorage().getCharacterByName(recipient.get("name"));
|
Character receiver = c.getChannelServer().getPlayerStorage().getCharacterByName(recipient.get("name"));
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
receiver.showNote();
|
noteService.show(receiver);
|
||||||
}
|
}
|
||||||
} else if (action == 0x05) { // Modify wish list
|
} else if (action == 0x05) { // Modify wish list
|
||||||
cs.clearWishList();
|
cs.clearWishList();
|
||||||
@@ -330,12 +335,8 @@ public final class CashOperationHandler extends AbstractPacketHandler {
|
|||||||
cs.gainCash(toCharge, itemRing, chr.getWorld());
|
cs.gainCash(toCharge, itemRing, chr.getWorld());
|
||||||
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
|
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
|
||||||
chr.addCrushRing(Ring.loadFromDb(rings.getLeft()));
|
chr.addCrushRing(Ring.loadFromDb(rings.getLeft()));
|
||||||
try {
|
noteService.sendWithFame(text, chr.getName(), partner.getName());
|
||||||
chr.sendNote(partner.getName(), text, (byte) 1);
|
noteService.show(partner);
|
||||||
} catch (SQLException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
partner.showNote();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -393,12 +394,8 @@ public final class CashOperationHandler extends AbstractPacketHandler {
|
|||||||
cs.gainCash(payment, -itemRing.getPrice());
|
cs.gainCash(payment, -itemRing.getPrice());
|
||||||
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
|
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
|
||||||
chr.addFriendshipRing(Ring.loadFromDb(rings.getLeft()));
|
chr.addFriendshipRing(Ring.loadFromDb(rings.getLeft()));
|
||||||
try {
|
noteService.sendWithFame(text, chr.getName(), partner.getName());
|
||||||
chr.sendNote(partner.getName(), text, (byte) 1);
|
noteService.show(partner);
|
||||||
} catch (SQLException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
partner.showNote();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ import net.packet.InPacket;
|
|||||||
* @author kevintjuh93
|
* @author kevintjuh93
|
||||||
*/
|
*/
|
||||||
public class FredrickHandler extends AbstractPacketHandler {
|
public class FredrickHandler extends AbstractPacketHandler {
|
||||||
|
private final FredrickProcessor fredrickProcessor;
|
||||||
|
|
||||||
|
public FredrickHandler(FredrickProcessor fredrickProcessor) {
|
||||||
|
this.fredrickProcessor = fredrickProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePacket(InPacket p, Client c) {
|
public void handlePacket(InPacket p, Client c) {
|
||||||
@@ -42,7 +47,7 @@ public class FredrickHandler extends AbstractPacketHandler {
|
|||||||
//c.sendPacket(PacketCreator.getFredrick((byte) 0x24));
|
//c.sendPacket(PacketCreator.getFredrick((byte) 0x24));
|
||||||
break;
|
break;
|
||||||
case 0x1A:
|
case 0x1A:
|
||||||
FredrickProcessor.fredrickRetrieveItems(c);
|
fredrickProcessor.fredrickRetrieveItems(c);
|
||||||
break;
|
break;
|
||||||
case 0x1C: //Exit
|
case 0x1C: //Exit
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -22,34 +22,40 @@
|
|||||||
package net.server.channel.handlers;
|
package net.server.channel.handlers;
|
||||||
|
|
||||||
import client.Client;
|
import client.Client;
|
||||||
|
import model.Note;
|
||||||
import net.AbstractPacketHandler;
|
import net.AbstractPacketHandler;
|
||||||
import net.packet.InPacket;
|
import net.packet.InPacket;
|
||||||
import tools.DatabaseConnection;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import service.NoteService;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.util.Optional;
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
public final class NoteActionHandler extends AbstractPacketHandler {
|
public final class NoteActionHandler extends AbstractPacketHandler {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(NoteActionHandler.class);
|
||||||
|
|
||||||
|
private final NoteService noteService;
|
||||||
|
|
||||||
|
public NoteActionHandler(NoteService noteService) {
|
||||||
|
this.noteService = noteService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handlePacket(InPacket p, Client c) {
|
public void handlePacket(InPacket p, Client c) {
|
||||||
int action = p.readByte();
|
int action = p.readByte();
|
||||||
if (action == 0 && c.getPlayer().getCashShop().getAvailableNotes() > 0) {
|
if (action == 0 && c.getPlayer().getCashShop().getAvailableNotes() > 0) { // Reply to gift in cash shop
|
||||||
String charname = p.readString();
|
String charname = p.readString();
|
||||||
String message = p.readString();
|
String message = p.readString();
|
||||||
try {
|
if (c.getPlayer().getCashShop().isOpened()) {
|
||||||
if (c.getPlayer().getCashShop().isOpened()) {
|
c.sendPacket(PacketCreator.showCashInventory(c));
|
||||||
c.sendPacket(PacketCreator.showCashInventory(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
c.getPlayer().sendNote(charname, message, (byte) 1);
|
|
||||||
c.getPlayer().getCashShop().decreaseNotes();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
} else if (action == 1) {
|
|
||||||
|
boolean sendNoteSuccess = noteService.sendWithFame(message, c.getPlayer().getName(), charname);
|
||||||
|
if (sendNoteSuccess) {
|
||||||
|
c.getPlayer().getCashShop().decreaseNotes();
|
||||||
|
}
|
||||||
|
} else if (action == 1) { // Discard notes in game
|
||||||
int num = p.readByte();
|
int num = p.readByte();
|
||||||
p.readByte();
|
p.readByte();
|
||||||
p.readByte();
|
p.readByte();
|
||||||
@@ -58,24 +64,13 @@ public final class NoteActionHandler extends AbstractPacketHandler {
|
|||||||
int id = p.readInt();
|
int id = p.readInt();
|
||||||
p.readByte(); //Fame, but we read it from the database :)
|
p.readByte(); //Fame, but we read it from the database :)
|
||||||
|
|
||||||
try (Connection con = DatabaseConnection.getConnection()) {
|
Optional<Note> discardedNote = noteService.delete(id);
|
||||||
try (PreparedStatement ps = con.prepareStatement("SELECT `fame` FROM notes WHERE id=? AND deleted=0")) {
|
if (discardedNote.isEmpty()) {
|
||||||
ps.setInt(1, id);
|
log.warn("Note with id {} not able to be discarded. Already discarded?", id);
|
||||||
try (ResultSet rs = ps.executeQuery()) {
|
continue;
|
||||||
if (rs.next()) {
|
|
||||||
fame += rs.getInt("fame");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try (PreparedStatement ps = con.prepareStatement("UPDATE notes SET `deleted` = 1 WHERE id = ?")) {
|
|
||||||
ps.setInt(1, id);
|
|
||||||
ps.executeUpdate();
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fame += discardedNote.get().fame();
|
||||||
}
|
}
|
||||||
if (fame > 0) {
|
if (fame > 0) {
|
||||||
c.getPlayer().gainFame(fame);
|
c.getPlayer().gainFame(fame);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import scripting.event.EventInstanceManager;
|
import scripting.event.EventInstanceManager;
|
||||||
import server.life.MobSkill;
|
import server.life.MobSkill;
|
||||||
|
import service.NoteService;
|
||||||
import tools.DatabaseConnection;
|
import tools.DatabaseConnection;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
@@ -63,6 +64,12 @@ public final class PlayerLoggedinHandler extends AbstractPacketHandler {
|
|||||||
private static final Logger log = LoggerFactory.getLogger(PlayerLoggedinHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(PlayerLoggedinHandler.class);
|
||||||
private static final Set<Integer> attemptingLoginAccounts = new HashSet<>();
|
private static final Set<Integer> attemptingLoginAccounts = new HashSet<>();
|
||||||
|
|
||||||
|
private final NoteService noteService;
|
||||||
|
|
||||||
|
public PlayerLoggedinHandler(NoteService noteService) {
|
||||||
|
this.noteService = noteService;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean tryAcquireAccount(int accId) {
|
private boolean tryAcquireAccount(int accId) {
|
||||||
synchronized (attemptingLoginAccounts) {
|
synchronized (attemptingLoginAccounts) {
|
||||||
if (attemptingLoginAccounts.contains(accId)) {
|
if (attemptingLoginAccounts.contains(accId)) {
|
||||||
@@ -302,7 +309,8 @@ public final class PlayerLoggedinHandler extends AbstractPacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
player.showNote();
|
noteService.show(player);
|
||||||
|
|
||||||
if (player.getParty() != null) {
|
if (player.getParty() != null) {
|
||||||
PartyCharacter pchar = player.getMPC();
|
PartyCharacter pchar = player.getMPC();
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import scripting.event.EventInstanceManager;
|
import scripting.event.EventInstanceManager;
|
||||||
import server.ItemInformationProvider;
|
import server.ItemInformationProvider;
|
||||||
|
import service.NoteService;
|
||||||
import tools.DatabaseConnection;
|
import tools.DatabaseConnection;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
@@ -56,6 +57,12 @@ import java.sql.SQLException;
|
|||||||
public final class RingActionHandler extends AbstractPacketHandler {
|
public final class RingActionHandler extends AbstractPacketHandler {
|
||||||
private static final Logger log = LoggerFactory.getLogger(RingActionHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(RingActionHandler.class);
|
||||||
|
|
||||||
|
private final NoteService noteService;
|
||||||
|
|
||||||
|
public RingActionHandler(NoteService noteService) {
|
||||||
|
this.noteService = noteService;
|
||||||
|
}
|
||||||
|
|
||||||
private static int getEngagementBoxId(int useItemId) {
|
private static int getEngagementBoxId(int useItemId) {
|
||||||
return switch (useItemId) {
|
return switch (useItemId) {
|
||||||
case ItemId.ENGAGEMENT_BOX_MOONSTONE -> ItemId.EMPTY_ENGAGEMENT_BOX_MOONSTONE;
|
case ItemId.ENGAGEMENT_BOX_MOONSTONE -> ItemId.EMPTY_ENGAGEMENT_BOX_MOONSTONE;
|
||||||
@@ -396,7 +403,8 @@ public final class RingActionHandler extends AbstractPacketHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String groom = c.getPlayer().getName(), bride = Character.getNameById(c.getPlayer().getPartnerId());
|
String groom = c.getPlayer().getName();
|
||||||
|
String bride = Character.getNameById(c.getPlayer().getPartnerId());
|
||||||
int guest = Character.getIdByName(name);
|
int guest = Character.getIdByName(name);
|
||||||
if (groom == null || bride == null || groom.equals("") || bride.equals("") || guest <= 0) {
|
if (groom == null || bride == null || groom.equals("") || bride.equals("") || guest <= 0) {
|
||||||
c.getPlayer().dropMessage(5, "Unable to find " + name + "!");
|
c.getPlayer().dropMessage(5, "Unable to find " + name + "!");
|
||||||
@@ -417,14 +425,16 @@ public final class RingActionHandler extends AbstractPacketHandler {
|
|||||||
if (resStatus > 0) {
|
if (resStatus > 0) {
|
||||||
long expiration = cserv.getWeddingTicketExpireTime(resStatus + 1);
|
long expiration = cserv.getWeddingTicketExpireTime(resStatus + 1);
|
||||||
|
|
||||||
|
String baseMessage = "You've been invited to %s and %s's Wedding!".formatted(groom, bride);
|
||||||
Character guestChr = c.getWorldServer().getPlayerStorage().getCharacterById(guest);
|
Character guestChr = c.getWorldServer().getPlayerStorage().getCharacterById(guest);
|
||||||
if (guestChr != null && InventoryManipulator.checkSpace(guestChr.getClient(), newItemId, 1, "") && InventoryManipulator.addById(guestChr.getClient(), newItemId, (short) 1, expiration)) {
|
if (guestChr != null && InventoryManipulator.checkSpace(guestChr.getClient(), newItemId, 1, "") && InventoryManipulator.addById(guestChr.getClient(), newItemId, (short) 1, expiration)) {
|
||||||
guestChr.dropMessage(6, "[Wedding] You've been invited to " + groom + " and " + bride + "'s Wedding!");
|
guestChr.dropMessage(6, "[Wedding] %s".formatted(baseMessage));
|
||||||
} else {
|
} else {
|
||||||
|
String dueyMessage = baseMessage + " Receive your invitation from Duey!";
|
||||||
if (guestChr != null && guestChr.isLoggedinWorld()) {
|
if (guestChr != null && guestChr.isLoggedinWorld()) {
|
||||||
guestChr.dropMessage(6, "[Wedding] You've been invited to " + groom + " and " + bride + "'s Wedding! Receive your invitation from Duey!");
|
guestChr.dropMessage(6, "[Wedding] %s".formatted(dueyMessage));
|
||||||
} else {
|
} else {
|
||||||
c.getPlayer().sendNote(name, "You've been invited to " + groom + " and " + bride + "'s Wedding! Receive your invitation from Duey!", (byte) 0);
|
noteService.sendNormal(dueyMessage, groom, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Item weddingTicket = new Item(newItemId, (short) 0, (short) 1);
|
Item weddingTicket = new Item(newItemId, (short) 0, (short) 1);
|
||||||
@@ -442,7 +452,7 @@ public final class RingActionHandler extends AbstractPacketHandler {
|
|||||||
c.getPlayer().dropMessage(5, "Invitation was not sent to '" + name + "'. Either the time for your marriage reservation already came or it was not found.");
|
c.getPlayer().dropMessage(5, "Invitation was not sent to '" + name + "'. Either the time for your marriage reservation already came or it was not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SQLException ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import constants.id.MapId;
|
|||||||
import constants.inventory.ItemConstants;
|
import constants.inventory.ItemConstants;
|
||||||
import net.AbstractPacketHandler;
|
import net.AbstractPacketHandler;
|
||||||
import net.packet.InPacket;
|
import net.packet.InPacket;
|
||||||
|
import net.packet.out.SendNoteSuccessPacket;
|
||||||
import net.server.Server;
|
import net.server.Server;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -46,10 +47,10 @@ import server.Shop;
|
|||||||
import server.ShopFactory;
|
import server.ShopFactory;
|
||||||
import server.TimerManager;
|
import server.TimerManager;
|
||||||
import server.maps.*;
|
import server.maps.*;
|
||||||
|
import service.NoteService;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -60,6 +61,12 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
|||||||
public final class UseCashItemHandler extends AbstractPacketHandler {
|
public final class UseCashItemHandler extends AbstractPacketHandler {
|
||||||
private static final Logger log = LoggerFactory.getLogger(UseCashItemHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(UseCashItemHandler.class);
|
||||||
|
|
||||||
|
private final NoteService noteService;
|
||||||
|
|
||||||
|
public UseCashItemHandler(NoteService noteService) {
|
||||||
|
this.noteService = noteService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePacket(InPacket p, Client c) {
|
public void handlePacket(InPacket p, Client c) {
|
||||||
final Character player = c.getPlayer();
|
final Character player = c.getPlayer();
|
||||||
@@ -358,12 +365,11 @@ public final class UseCashItemHandler extends AbstractPacketHandler {
|
|||||||
} else if (itemType == 509) {
|
} else if (itemType == 509) {
|
||||||
String sendTo = p.readString();
|
String sendTo = p.readString();
|
||||||
String msg = p.readString();
|
String msg = p.readString();
|
||||||
try {
|
boolean sendSuccess = noteService.sendNormal(msg, player.getName(), sendTo);
|
||||||
player.sendNote(sendTo, msg, (byte) 0);
|
if (sendSuccess) {
|
||||||
} catch (SQLException e) {
|
remove(c, position, itemId);
|
||||||
e.printStackTrace();
|
c.sendPacket(new SendNoteSuccessPacket());
|
||||||
}
|
}
|
||||||
remove(c, position, itemId);
|
|
||||||
} else if (itemType == 510) {
|
} else if (itemType == 510) {
|
||||||
player.getMap().broadcastMessage(PacketCreator.musicChange("Jukebox/Congratulation"));
|
player.getMap().broadcastMessage(PacketCreator.musicChange("Jukebox/Congratulation"));
|
||||||
remove(c, position, itemId);
|
remove(c, position, itemId);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import net.server.coordinator.world.InviteCoordinator.InviteResult;
|
|||||||
import net.server.coordinator.world.InviteCoordinator.InviteType;
|
import net.server.coordinator.world.InviteCoordinator.InviteType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import service.NoteService;
|
||||||
import tools.DatabaseConnection;
|
import tools.DatabaseConnection;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
|
|
||||||
@@ -497,7 +498,7 @@ public class Guild {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expelMember(GuildCharacter initiator, String name, int cid) {
|
public void expelMember(GuildCharacter initiator, String name, int cid, NoteService noteService) {
|
||||||
membersLock.lock();
|
membersLock.lock();
|
||||||
try {
|
try {
|
||||||
java.util.Iterator<GuildCharacter> itr = members.iterator();
|
java.util.Iterator<GuildCharacter> itr = members.iterator();
|
||||||
@@ -512,16 +513,7 @@ public class Guild {
|
|||||||
if (mgc.isOnline()) {
|
if (mgc.isOnline()) {
|
||||||
Server.getInstance().getWorld(mgc.getWorld()).setGuildAndRank(cid, 0, 5);
|
Server.getInstance().getWorld(mgc.getWorld()).setGuildAndRank(cid, 0, 5);
|
||||||
} else {
|
} else {
|
||||||
try (Connection con = DatabaseConnection.getConnection();
|
noteService.sendNormal("You have been expelled from the guild.", initiator.getName(), mgc.getName());
|
||||||
PreparedStatement ps = con.prepareStatement("INSERT INTO notes (`to`, `from`, `message`, `timestamp`) VALUES (?, ?, ?, ?)")) {
|
|
||||||
ps.setString(1, mgc.getName());
|
|
||||||
ps.setString(2, initiator.getName());
|
|
||||||
ps.setString(3, "You have been expelled from the guild.");
|
|
||||||
ps.setLong(4, System.currentTimeMillis());
|
|
||||||
ps.executeUpdate();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
log.error("expelMember - Guild", e);
|
|
||||||
}
|
|
||||||
Server.getInstance().getWorld(mgc.getWorld()).setOfflineGuildStatus((short) 0, (byte) 5, cid);
|
Server.getInstance().getWorld(mgc.getWorld()).setOfflineGuildStatus((short) 0, (byte) 5, cid);
|
||||||
}
|
}
|
||||||
} catch (Exception re) {
|
} catch (Exception re) {
|
||||||
|
|||||||
@@ -26,10 +26,15 @@ import client.processor.npc.FredrickProcessor;
|
|||||||
* @author Ronan
|
* @author Ronan
|
||||||
*/
|
*/
|
||||||
public class DueyFredrickTask implements Runnable {
|
public class DueyFredrickTask implements Runnable {
|
||||||
|
private final FredrickProcessor fredrickProcessor;
|
||||||
|
|
||||||
|
public DueyFredrickTask(FredrickProcessor fredrickProcessor) {
|
||||||
|
this.fredrickProcessor = fredrickProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
FredrickProcessor.runFredrickSchedule();
|
fredrickProcessor.runFredrickSchedule();
|
||||||
DueyProcessor.runDueyExpireSchedule();
|
DueyProcessor.runDueyExpireSchedule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
110
src/main/java/service/NoteService.java
Normal file
110
src/main/java/service/NoteService.java
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package service;
|
||||||
|
|
||||||
|
import client.Character;
|
||||||
|
import database.DaoException;
|
||||||
|
import database.note.NoteDao;
|
||||||
|
import model.Note;
|
||||||
|
import net.packet.out.ShowNotesPacket;
|
||||||
|
import net.server.Server;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class NoteService {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(NoteService.class);
|
||||||
|
|
||||||
|
private final NoteDao noteDao;
|
||||||
|
|
||||||
|
public NoteService(NoteDao noteDao) {
|
||||||
|
this.noteDao = noteDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send normal note from one character to another
|
||||||
|
*
|
||||||
|
* @return Send success
|
||||||
|
*/
|
||||||
|
public boolean sendNormal(String message, String senderName, String receiverName) {
|
||||||
|
Note normalNote = Note.createNormal(message, senderName, receiverName, Server.getInstance().getCurrentTime());
|
||||||
|
return send(normalNote);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send note which will increase the receiver's fame by one.
|
||||||
|
*
|
||||||
|
* @return Send success
|
||||||
|
*/
|
||||||
|
public boolean sendWithFame(String message, String senderName, String receiverName) {
|
||||||
|
Note noteWithFame = Note.createGift(message, senderName, receiverName, Server.getInstance().getCurrentTime());
|
||||||
|
return send(noteWithFame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean send(Note note) {
|
||||||
|
// TODO: handle the following cases (originally listed at PacketCreator#noteError)
|
||||||
|
/*
|
||||||
|
* 0 = Player online, use whisper
|
||||||
|
* 1 = Check player's name
|
||||||
|
* 2 = Receiver inbox full
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
noteDao.save(note);
|
||||||
|
return true;
|
||||||
|
} catch (DaoException e) {
|
||||||
|
log.error("Failed to send note {}", note, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show unread notes
|
||||||
|
*
|
||||||
|
* @param chr Note recipient
|
||||||
|
*/
|
||||||
|
public void show(Character chr) {
|
||||||
|
if (chr == null) {
|
||||||
|
throw new IllegalArgumentException("Unable to show notes - chr is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Note> notes = getNotes(chr.getName());
|
||||||
|
if (notes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chr.sendPacket(new ShowNotesPacket(notes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Note> getNotes(String to) {
|
||||||
|
final List<Note> notes;
|
||||||
|
try {
|
||||||
|
notes = noteDao.findAllByTo(to);
|
||||||
|
} catch (DaoException e) {
|
||||||
|
log.error("Failed to find notes sent to chr name {}", to, e);
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notes == null || notes.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a read note
|
||||||
|
*
|
||||||
|
* @param noteId Id of note to discard
|
||||||
|
* @return Discarded note. Empty optional if failed to discard.
|
||||||
|
*/
|
||||||
|
public Optional<Note> delete(int noteId) {
|
||||||
|
try {
|
||||||
|
return noteDao.delete(noteId);
|
||||||
|
} catch (DaoException e) {
|
||||||
|
log.error("Failed to discard note with id {}", noteId, e);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,9 +3,13 @@ package tools;
|
|||||||
import com.zaxxer.hikari.HikariConfig;
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
|
import database.note.NoteRowMapper;
|
||||||
|
import org.jdbi.v3.core.Handle;
|
||||||
|
import org.jdbi.v3.core.Jdbi;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -21,15 +25,24 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
|||||||
public class DatabaseConnection {
|
public class DatabaseConnection {
|
||||||
private static final Logger log = LoggerFactory.getLogger(DatabaseConnection.class);
|
private static final Logger log = LoggerFactory.getLogger(DatabaseConnection.class);
|
||||||
private static HikariDataSource dataSource;
|
private static HikariDataSource dataSource;
|
||||||
|
private static Jdbi jdbi;
|
||||||
|
|
||||||
public static Connection getConnection() throws SQLException {
|
public static Connection getConnection() throws SQLException {
|
||||||
if (dataSource == null) {
|
if (dataSource == null) {
|
||||||
throw new IllegalStateException("Unable to get connection from uninitialized connection pool");
|
throw new IllegalStateException("Unable to get connection - connection pool is uninitialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataSource.getConnection();
|
return dataSource.getConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Handle getHandle() {
|
||||||
|
if (jdbi == null) {
|
||||||
|
throw new IllegalStateException("Unable to get handle - connection pool is uninitialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
return jdbi.open();
|
||||||
|
}
|
||||||
|
|
||||||
private static String getDbUrl() {
|
private static String getDbUrl() {
|
||||||
// Environment variables override what's defined in the config file
|
// Environment variables override what's defined in the config file
|
||||||
// This feature is used for the Docker support
|
// This feature is used for the Docker support
|
||||||
@@ -73,6 +86,7 @@ public class DatabaseConnection {
|
|||||||
Instant initStart = Instant.now();
|
Instant initStart = Instant.now();
|
||||||
try {
|
try {
|
||||||
dataSource = new HikariDataSource(config);
|
dataSource = new HikariDataSource(config);
|
||||||
|
initializeJdbi(dataSource);
|
||||||
long initDuration = Duration.between(initStart, Instant.now()).toMillis();
|
long initDuration = Duration.between(initStart, Instant.now()).toMillis();
|
||||||
log.info("Connection pool initialized in {} ms", initDuration);
|
log.info("Connection pool initialized in {} ms", initDuration);
|
||||||
return true;
|
return true;
|
||||||
@@ -84,4 +98,9 @@ public class DatabaseConnection {
|
|||||||
// Timed out - failed to initialize
|
// Timed out - failed to initialize
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void initializeJdbi(DataSource dataSource) {
|
||||||
|
jdbi = Jdbi.create(dataSource)
|
||||||
|
.registerRowMapper(new NoteRowMapper());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ import server.movement.LifeMovementFragment;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -5404,12 +5403,6 @@ public class PacketCreator {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Packet noteSendMsg() {
|
|
||||||
OutPacket p = OutPacket.create(SendOpcode.MEMO_RESULT);
|
|
||||||
p.writeByte(4);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 0 = Player online, use whisper
|
* 0 = Player online, use whisper
|
||||||
* 1 = Check player's name
|
* 1 = Check player's name
|
||||||
@@ -5422,21 +5415,6 @@ public class PacketCreator {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Packet showNotes(ResultSet notes, int count) throws SQLException {
|
|
||||||
final OutPacket p = OutPacket.create(SendOpcode.MEMO_RESULT);
|
|
||||||
p.writeByte(3);
|
|
||||||
p.writeByte(count);
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
p.writeInt(notes.getInt("id"));
|
|
||||||
p.writeString(notes.getString("from") + " ");//Stupid nexon forgot space lol
|
|
||||||
p.writeString(notes.getString("message"));
|
|
||||||
p.writeLong(getTime(notes.getLong("timestamp")));
|
|
||||||
p.writeByte(notes.getByte("fame"));//FAME :D
|
|
||||||
notes.next();
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Packet useChalkboard(Character chr, boolean close) {
|
public static Packet useChalkboard(Character chr, boolean close) {
|
||||||
OutPacket p = OutPacket.create(SendOpcode.CHALKBOARD);
|
OutPacket p = OutPacket.create(SendOpcode.CHALKBOARD);
|
||||||
p.writeInt(chr.getId());
|
p.writeInt(chr.getId());
|
||||||
|
|||||||
28
src/test/java/model/NoteTest.java
Normal file
28
src/test/java/model/NoteTest.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package model;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class NoteTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void requireNonNullMessage() {
|
||||||
|
assertThrows(NullPointerException.class, () -> new Note(1, null, "from", "to", System.currentTimeMillis(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void requireNonNullFrom() {
|
||||||
|
assertThrows(NullPointerException.class, () -> new Note(2, "message", null, "to", System.currentTimeMillis(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void requireNonNullTo() {
|
||||||
|
assertThrows(NullPointerException.class, () -> new Note(3, "message", "from", null, System.currentTimeMillis(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createNew() {
|
||||||
|
assertDoesNotThrow(() -> new Note(4, "message", "from", "to", System.currentTimeMillis(), 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
142
src/test/java/service/NoteServiceTest.java
Normal file
142
src/test/java/service/NoteServiceTest.java
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package service;
|
||||||
|
|
||||||
|
import database.note.NoteDao;
|
||||||
|
import model.Note;
|
||||||
|
import net.packet.out.ShowNotesPacket;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import testutil.Mocks;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
import static testutil.AnyValues.daoException;
|
||||||
|
import static testutil.AnyValues.string;
|
||||||
|
|
||||||
|
class NoteServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private NoteDao noteDao;
|
||||||
|
|
||||||
|
private NoteService noteService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void reset() {
|
||||||
|
MockitoAnnotations.openMocks(this);
|
||||||
|
this.noteService = new NoteService(noteDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sendNormalSuccess() {
|
||||||
|
String message = "message";
|
||||||
|
String from = "from";
|
||||||
|
String to = "to";
|
||||||
|
|
||||||
|
boolean success = noteService.sendNormal(message, from, to);
|
||||||
|
|
||||||
|
assertTrue(success);
|
||||||
|
var noteCaptor = ArgumentCaptor.forClass(Note.class);
|
||||||
|
verify(noteDao).save(noteCaptor.capture());
|
||||||
|
var note = noteCaptor.getValue();
|
||||||
|
assertEquals(message, note.message());
|
||||||
|
assertEquals(from, note.from());
|
||||||
|
assertEquals(to, note.to());
|
||||||
|
assertEquals(0, note.fame());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sendWithFameSuccess() {
|
||||||
|
String message = "fameMessage";
|
||||||
|
String from = "fameFrom";
|
||||||
|
String to = "fameTo";
|
||||||
|
|
||||||
|
boolean success = noteService.sendWithFame(message, from, to);
|
||||||
|
|
||||||
|
assertTrue(success);
|
||||||
|
var noteCaptor = ArgumentCaptor.forClass(Note.class);
|
||||||
|
verify(noteDao).save(noteCaptor.capture());
|
||||||
|
var note = noteCaptor.getValue();
|
||||||
|
assertEquals(message, note.message());
|
||||||
|
assertEquals(from, note.from());
|
||||||
|
assertEquals(to, note.to());
|
||||||
|
assertEquals(1, note.fame());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sendFailure() {
|
||||||
|
doThrow(daoException()).when(noteDao).save(any());
|
||||||
|
|
||||||
|
boolean success = noteService.sendNormal(string(), string(), string());
|
||||||
|
|
||||||
|
assertFalse(success);
|
||||||
|
verify(noteDao).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void showRejectsNull() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> noteService.show(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void showOneNote() {
|
||||||
|
String chrName = "showMeNotes";
|
||||||
|
var chr = Mocks.chr(chrName);
|
||||||
|
when(noteDao.findAllByTo(chrName)).thenReturn(List.of(anyNote()));
|
||||||
|
|
||||||
|
noteService.show(chr);
|
||||||
|
|
||||||
|
verify(chr).sendPacket(any(ShowNotesPacket.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Note anyNote() {
|
||||||
|
return new Note(1, "message", "from", "to", 100200300400L, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void showZeroNotes_shouldNotSendPacket() {
|
||||||
|
var chr = Mocks.chr("mockChr");
|
||||||
|
when(noteDao.findAllByTo(any())).thenReturn(Collections.emptyList());
|
||||||
|
|
||||||
|
noteService.show(chr);
|
||||||
|
|
||||||
|
verify(chr, never()).sendPacket(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void showNotesFailure_shouldNotSendPacket() {
|
||||||
|
var chr = Mocks.chr("mockChr");
|
||||||
|
when(noteDao.findAllByTo(any())).thenThrow(daoException());
|
||||||
|
|
||||||
|
noteService.show(chr);
|
||||||
|
|
||||||
|
verify(chr, never()).sendPacket(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteNoteSuccess() {
|
||||||
|
int noteId = 1056;
|
||||||
|
var note = anyNote();
|
||||||
|
when(noteDao.delete(noteId)).thenReturn(Optional.of(note));
|
||||||
|
|
||||||
|
Optional<Note> deletedNote = noteDao.delete(noteId);
|
||||||
|
|
||||||
|
assertTrue(deletedNote.isPresent());
|
||||||
|
assertEquals(note, deletedNote.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteNoteFailure() {
|
||||||
|
when(noteDao.delete(anyInt())).thenThrow(daoException());
|
||||||
|
|
||||||
|
Optional<Note> deletedNote = noteService.delete(4382);
|
||||||
|
|
||||||
|
assertTrue(deletedNote.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/test/java/testutil/AnyValues.java
Normal file
14
src/test/java/testutil/AnyValues.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package testutil;
|
||||||
|
|
||||||
|
import database.DaoException;
|
||||||
|
|
||||||
|
public class AnyValues {
|
||||||
|
|
||||||
|
public static String string() {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DaoException daoException() {
|
||||||
|
return new DaoException(string(), new RuntimeException());
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/test/java/testutil/Mocks.java
Normal file
19
src/test/java/testutil/Mocks.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package testutil;
|
||||||
|
|
||||||
|
import client.Character;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class Mocks {
|
||||||
|
|
||||||
|
public static Character chr() {
|
||||||
|
return Mockito.mock(Character.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Character chr(String name) {
|
||||||
|
var chr = chr();
|
||||||
|
when(chr.getName()).thenReturn(name);
|
||||||
|
return chr;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/test/resources/log4j2-test.xml
Normal file
20
src/test/resources/log4j2-test.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="WARN" name="Cosmic" shutdownHook="disable">
|
||||||
|
<Properties>
|
||||||
|
<Property name="standard-pattern">%d{HH:mm:ss.SSS} [%t] %-5level %logger{2} - %msg%n</Property>
|
||||||
|
</Properties>
|
||||||
|
|
||||||
|
<Appenders>
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout>
|
||||||
|
<Pattern>${standard-pattern}</Pattern>
|
||||||
|
</PatternLayout>
|
||||||
|
</Console>
|
||||||
|
</Appenders>
|
||||||
|
|
||||||
|
<Loggers>
|
||||||
|
<Root level="off">
|
||||||
|
<AppenderRef ref="Console" level="info"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
Reference in New Issue
Block a user