L2JMobius

Public Development => Shares/Contributions => Topic started by: mastermind007 on April 08, 2026, 02:11:17 AM

Title: Introducing Ghost Services
Post by: mastermind007 on April 08, 2026, 02:11:17 AM
Hello All,

At Mobius request I convert the Ghost Services into a standalone module.
You can access it here https://gitlab.com/Mastermind007/l2jmobius-ghost-services.git (https://gitlab.com/Mastermind007/l2jmobius-ghost-services.git)

The readme file has all the detail but the short version is add the GhostServices folder to your servers "data/scripts/custom".
Go to the config folder and enable the shops, manufacture and wanderers (change from False to True).
Start your server as normal.

Now that this module is not coded in the care it should be easier to test with other chronicles.
 

--Mastermind007

EDIT: Here is a video showing the mod in use. This is spawning 200 shops between Giran and Dion. The characters you see running around are the Ghost Wanderers that will buy from real player shops only, if the price is right.

https://youtu.be/v-Uf8ErwQz4 (https://youtu.be/v-Uf8ErwQz4)
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 09, 2026, 03:42:13 PM
I'm having trouble adding high-value items. I get an error about the format being incorrect. How can I fix this?
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 09, 2026, 05:28:16 PM
I'm having trouble adding high-value items. I get an error about the format being incorrect. How can I fix this?

Not sure how you are adding items but I am guessing it is using the shopitems.xml file. Make sure to follow the layout of an existing item in the same category exaactly.

Code: [Select]
<item id="299" name="Orcish Poleaxe" category="Weapon"><MinCount val="1" /><MaxCount val="1" /><BuyLow val="2800000" /><BuyHigh val="3500000" /><SellLow val="3800000" /><SellHigh val="5000000" /><Level val="1" /></item>   
Make sure the category matches exactly as they are used in the code.
Make sure the counts (min/max), as well as the Buy/Sell low and high also have a value. They are quoted in the XML but need to be numbers.
Lastly the Level is a number assigned to the item grade. 1 through 5 equals D through S grade.  This is used to limit the type of items available in Dion only. Every item as a grade associated, even craft mats. Dion gets 1 and 2 (D and C grade) only. Giran has no restriction

It you share exactly what you are wanting to add and what you have so far, I am sure we can get it figured out.

EDIT: This will make life a little easier. This link is for a MySQL dump of the current shopitems table used to create the XML items.  https://pastebin.com/kQf0PPdr (https://pastebin.com/kQf0PPdr)

Add your new items to the table using your favorite MySQL tool. You can also update prices on existing items to fit your server
When finished the following query will output the items in the correct XML format. If you only want to create one or two just add a where clause to the query so it only outputs those items. All that is left is to paste then in the XML file and reload the server.

Code: [Select]
SELECT
    CONCAT(
        '<item id="', id, '" name="', NAME, '" category="', category, '">',
        '<MinCount val="', MinCount, '" />',
        '<MaxCount val="', MaxCount, '" />',
        '<BuyLow val="', BuyLow, '" />',
        '<BuyHigh val="', BuyHigh, '" />',
        '<SellLow val="', SellLow, '" />',
        '<SellHigh val="', SellHigh, '" />',
        '<Level val="', LEVEL, '" />',
        '</item>'
    ) AS xml_row
FROM shopitems;
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 09, 2026, 08:53:24 PM
Not sure how you are adding items but I am guessing it is using the shopitems.xml file. Make sure to follow the layout of an existing item in the same category exaactly.

Code: [Select]
<item id="299" name="Orcish Poleaxe" category="Weapon"><MinCount val="1" /><MaxCount val="1" /><BuyLow val="2800000" /><BuyHigh val="3500000" /><SellLow val="3800000" /><SellHigh val="5000000" /><Level val="1" /></item>   
Make sure the category matches exactly as they are used in the code.
Make sure the counts (min/max), as well as the Buy/Sell low and high also have a value. They are quoted in the XML but need to be numbers.
Lastly the Level is a number assigned to the item grade. 1 through 5 equals D through S grade.  This is used to limit the type of items available in Dion only. Every item as a grade associated, even craft mats. Dion gets 1 and 2 (D and C grade) only. Giran has no restriction

It you share exactly what you are wanting to add and what you have so far, I am sure we can get it figured out.

EDIT: This will make life a little easier. This link is for a MySQL dump of the current shopitems table used to create the XML items.  https://pastebin.com/kQf0PPdr (https://pastebin.com/kQf0PPdr)

Add your new items to the table using your favorite MySQL tool. You can also update prices on existing items to fit your server
When finished the following query will output the items in the correct XML format. If you only want to create one or two just add a where clause to the query so it only outputs those items. All that is left is to paste then in the XML file and reload the server.

Code: [Select]
SELECT
    CONCAT(
        '<item id="', id, '" name="', NAME, '" category="', category, '">',
        '<MinCount val="', MinCount, '" />',
        '<MaxCount val="', MaxCount, '" />',
        '<BuyLow val="', BuyLow, '" />',
        '<BuyHigh val="', BuyHigh, '" />',
        '<SellLow val="', SellLow, '" />',
        '<SellHigh val="', SellHigh, '" />',
        '<Level val="', LEVEL, '" />',
        '</item>'
    ) AS xml_row
FROM shopitems;
this value gives an error:
Code: [Select]
<BuyLow val="6062212500" /><BuyHigh val="7062212500" /><SellLow val="6062212500" /><SellHigh val="7062212500" />Writes this error:
Code: [Select]
[main] ERROR org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData - GhostShopItemData: Error loading Shop XML:
java.lang.NumberFormatException: For input string: "6062212500"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:527)
at java.base/java.lang.Integer.parseInt(Integer.java:624)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData.parseItem(GhostShopItemData.java:80)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData.load(GhostShopItemData.java:50)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData.<init>(GhostShopItemData.java:31)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData$SingletonHolder.<clinit>(GhostShopItemData.java:125)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData.getInstance(GhostShopItemData.java:102)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.applyItemsToOfflineShop(GhostShopManager.java:331)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.createOfflineGhostShop(GhostShopManager.java:189)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.createGhostShopRandom(GhostShopManager.java:140)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.startBatchSpawning(GhostShopManager.java:589)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.initStartupSpawns(GhostShopManager.java:570)
at org.l2jmobius.gameserver.GameServer.<init>(GameServer.java:510)
at org.l2jmobius.gameserver.GameServer.main(GameServer.java:601)
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 10, 2026, 01:23:18 AM
this value gives an error:
Code: [Select]
<BuyLow val="6062212500" /><BuyHigh val="7062212500" /><SellLow val="6062212500" /><SellHigh val="7062212500" />Writes this error:
Code: [Select]
[main] ERROR org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData - GhostShopItemData: Error loading Shop XML:
java.lang.NumberFormatException: For input string: "6062212500"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:527)
at java.base/java.lang.Integer.parseInt(Integer.java:624)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData.parseItem(GhostShopItemData.java:80)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData.load(GhostShopItemData.java:50)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData.<init>(GhostShopItemData.java:31)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData$SingletonHolder.<clinit>(GhostShopItemData.java:125)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopItemData.getInstance(GhostShopItemData.java:102)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.applyItemsToOfflineShop(GhostShopManager.java:331)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.createOfflineGhostShop(GhostShopManager.java:189)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.createGhostShopRandom(GhostShopManager.java:140)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.startBatchSpawning(GhostShopManager.java:589)
at org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopManager.initStartupSpawns(GhostShopManager.java:570)
at org.l2jmobius.gameserver.GameServer.<init>(GameServer.java:510)
at org.l2jmobius.gameserver.GameServer.main(GameServer.java:601)

That price is way higher than expected. 6 billion adena? Is that correct or a typo?
The cause it the parser uses a standard integer which in java has a max value of  2,147,483,647

To allow for such high values the code would need changed in both GhostShopItem.java and GhostShopItemData.java, changing int to long for the buy and sell low/high. I am not in a position to do that now but maybe tomorrow I can take a look.
Title: Re: Introducing Ghost Services
Post by: BazookaRpm on April 10, 2026, 02:58:38 AM
It's a good contribution; it would be good to adapt them to all projects.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 10, 2026, 09:39:06 AM
That price is way higher than expected. 6 billion adena? Is that correct or a typo?
The cause it the parser uses a standard integer which in java has a max value of  2,147,483,647

To allow for such high values the code would need changed in both GhostShopItem.java and GhostShopItemData.java, changing int to long for the buy and sell low/high. I am not in a position to do that now but maybe tomorrow I can take a look.


Yes, that's exactly the value. I implemented this mod into the Wolf Vagr Chronicles. And that's the price of the items there.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 10, 2026, 09:42:42 AM
That price is way higher than expected. 6 billion adena? Is that correct or a typo?
The cause it the parser uses a standard integer which in java has a max value of  2,147,483,647

To allow for such high values the code would need changed in both GhostShopItem.java and GhostShopItemData.java, changing int to long for the buy and sell low/high. I am not in a position to do that now but maybe tomorrow I can take a look.

and I changed int to long in this file, but nothing changed, the error is still there.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 10, 2026, 03:44:09 PM
and I changed int to long in this file, but nothing changed, the error is still there.

There are multiple places the int needed changed to long.
I made the change and push a commit to the repo mention in my initial post.

Did adding this mod to Wolf Vagr give you any issues, other than the price value?
Was hoping the code for newer chronicles did not change much so it would be usable with them.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 10, 2026, 05:05:53 PM
There are multiple places the int needed changed to long.
I made the change and push a commit to the repo mention in my initial post.

Did adding this mod to Wolf Vagr give you any issues, other than the price value?
Was hoping the code for newer chronicles did not change much so it would be usable with them.

The only problem was the price.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 17, 2026, 01:31:56 PM
There's an issue with replacing merchants and ghost buyers. When it comes time to replace existing merchants and buyers, the system doesn't replace them, but simply adds new ones. What's responsible for removing ghost merchants and buyers from the game and replacing them with new ones?
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 17, 2026, 04:40:14 PM
There's an issue with replacing merchants and ghost buyers. When it comes time to replace existing merchants and buyers, the system doesn't replace them, but simply adds new ones. What's responsible for removing ghost merchants and buyers from the game and replacing them with new ones?

Shops:
Towards the end of the "createOfflineGhostShop" there is the line             
Code: [Select]
schedulePurge(ghost.getObjectId(), name, Rnd.get(ROTATION_MIN_HOURS, ROTATION_MAX_HOURS) * ROTATION_MAX_HOURS_MULTIPLIER);
The "schedulePurge" creates that actual timer for the shop to remove the it and start a replacement shop.
You should soo the message in the server log when this fires
Code: [Select]
LOG.info("Rotated out {} (ID: {})", name, objectId);
Buyers:
They follow a similar approach but beings there are far fewer of then that shops they rotate out as a group. The "scheduleNextRotation()" establishes the schedule based on the min/max hours set in the config file. When the event fires all are replaced with new ones.
When this fires you should see a server log message
Code: [Select]
LOG.info("Batch rotated successfully.");
I guess to start are you seeing any of this messages in the log?
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 17, 2026, 06:54:17 PM
Shops:
Towards the end of the "createOfflineGhostShop" there is the line             
Code: [Select]
schedulePurge(ghost.getObjectId(), name, Rnd.get(ROTATION_MIN_HOURS, ROTATION_MAX_HOURS) * ROTATION_MAX_HOURS_MULTIPLIER);
The "schedulePurge" creates that actual timer for the shop to remove the it and start a replacement shop.
You should soo the message in the server log when this fires
Code: [Select]
LOG.info("Rotated out {} (ID: {})", name, objectId);
Buyers:
They follow a similar approach but beings there are far fewer of then that shops they rotate out as a group. The "scheduleNextRotation()" establishes the schedule based on the min/max hours set in the config file. When the event fires all are replaced with new ones.
When this fires you should see a server log message
Code: [Select]
LOG.info("Batch rotated successfully.");
I guess to start are you seeing any of this messages in the log?

Yes, the server console says everything went well, but in fact it added new ones and didn't delete the old ones.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 17, 2026, 07:58:44 PM
Code: [Select]
:07] New maximum connected count of 643!
[L2jMobius ScheduledThread 48] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Ithenlan (ID: 268513085)
[17/04 19:55:09] New maximum connected count of 644!
[L2jMobius ScheduledThread 43] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopRunaManager - Rotated out Елонбане (ID: 268515633)
[L2jMobius ScheduledThread 9] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Faerentide (ID: 268511695)
[17/04 19:55:12] New maximum connected count of 645!
[L2jMobius ScheduledThread 53] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopRunaManager - Rotated out Силиус (ID: 268512434)
[17/04 19:55:14] New maximum connected count of 647!
[L2jMobius ScheduledThread 75] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Zansong (ID: 268515750)
[L2jMobius ScheduledThread 40] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopRunaManager - Rotated out Валбане (ID: 268512855)
[L2jMobius ScheduledThread 49] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Ithius (ID: 268515995)
[17/04 19:55:21] New maximum connected count of 648!
[L2jMobius ScheduledThread 76] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopRunaManager - Rotated out Кинтхас (ID: 268512553)
[L2jMobius ScheduledThread 6] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopRunaManager - Rotated out Оникстхас (ID: 268510859)
[17/04 19:55:24] New maximum connected count of 650!
[L2jMobius ScheduledThread 39] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Syldar (ID: 268516175)
[L2jMobius ScheduledThread 2] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Bravorwyn (ID: 268510236)
[L2jMobius ScheduledThread 11] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Azislith (ID: 268513410)
[L2jMobius ScheduledThread 77] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopRunaManager - Rotated out Рхуниррим (ID: 268511449)
[L2jMobius ScheduledThread 36] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Cynmorn (ID: 268513404)
[L2jMobius ScheduledThread 14] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Indarvane (ID: 268513134)
[17/04 19:55:28] New maximum connected count of 651!
[L2jMobius ScheduledThread 58] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopGiranManager - Rotated out Faeronbore (ID: 268511972)
[L2jMobius ScheduledThread 49] INFO org.l2jmobius.gameserver.ghostservices.ghostshops.GhostShopRunaManager - Rotated out Ваелумиус (ID: 268515751)
[17/04 19:55:33] New maximum connected count of 653!
[17/04 19:55:36] New maximum connected count of 654!
[17/04 19:55:37] New maximum connected count of 655!
[17/04 19:55:39] New maximum connected count of 656!


This is what the console says and continues to increase the maximum online...
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 17, 2026, 08:04:30 PM
And the most interesting thing is, the database only lists 150 ghost shops and 30 runners. But in fact, there are already over 1,000 of them in the game... and the number is growing.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 18, 2026, 08:32:27 PM
I also noticed that if you try to sell several items to a merchant, nothing happens. If you sell one item at a time, everything is fine. I don't think that's how it should be.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 18, 2026, 11:12:56 PM
I pushed a fix to the repo. Shops and wanderers are now fully removed when there time is up.

No sure about the merchant issue. The ghosts do not touch that part of the core.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 20, 2026, 07:26:18 AM
I pushed a fix to the repo. Shops and wanderers are now fully removed when there time is up.

No sure about the merchant issue. The ghosts do not touch that part of the core.

Everything works like clockwork now! A very good solution for servers with low online traffic. Thanks for your work.
It would also be great to introduce combat characters who run around the locations and level up. And you can PvP or PK with them. ))) But that's just a rant.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 22, 2026, 09:22:29 PM
A new problem has arisen. I set Max_Batches to 100, and I have a total of 900+ merchants and walking customers. When it's time to replace merchants and walking customers, they are deleted faster than they are replaced, and eventually, within 24 hours, the number drops from 900+ to 150-250. The settings require a minimum of 1 minute before adding a new merchant. This is a lot... they delete faster than they add new ones.
Title: Re: Introducing Ghost Services
Post by: GuruGel on April 22, 2026, 09:27:38 PM
I changed the value Respawn_Delay=1 to Respawn_Delay=0.01 and accordingly in the kernel I also changed the value to double, but it did not help, the deletion occurs the same way, and there are practically no new additions.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 23, 2026, 03:25:56 AM
I changed the value Respawn_Delay=1 to Respawn_Delay=0.01 and accordingly in the kernel I also changed the value to double, but it did not help, the deletion occurs the same way, and there are practically no new additions.

Wow 900+. I can honestly say I never tried that many. Think around 300 was the most I ever tried. I am surprised most of them spawned. There is a location aware method in the code that keeps a little separation between players and other ghosts. They will try to find an open spot 3 times and then they give up. You could be running into a situation where there are many not finding a spot and never spawning. That kills the schedule replacement so over time the server is find a balance. If you get enough of those the population number will drop. I bet you are seeing a fair amount of tried 3 times and failed messages in the log.

Title: Re: Introducing Ghost Services
Post by: GuruGel on April 23, 2026, 08:53:57 PM
Wow 900+. I can honestly say I never tried that many. Think around 300 was the most I ever tried. I am surprised most of them spawned. There is a location aware method in the code that keeps a little separation between players and other ghosts. They will try to find an open spot 3 times and then they give up. You could be running into a situation where there are many not finding a spot and never spawning. That kills the schedule replacement so over time the server is find a balance. If you get enough of those the population number will drop. I bet you are seeing a fair amount of tried 3 times and failed messages in the log.

In this situation, there's a solution: eliminate character replacement altogether. They will only be replaced after the character has sold everything they were selling.
Title: Re: Introducing Ghost Services
Post by: Mobius on April 25, 2026, 01:24:11 PM
Any chance this system can be done only on datapack using listeners?
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 27, 2026, 11:22:05 PM
Any chance this system can be done only on datapack using listeners?

In theory, Yes.
The system is mostly self contained as it is. The only part I am not sure about it the purging of old data from the offline trade tables in the even of a server crash. If the "OFFLINE_GHOST" are not removed before the core's native offline shops they will spawn. I guess in the Init() they could be purged and no one would really know as the game server would not be connected to the login server yet.

I have been out of the L2 scene for a long time and I am new to Mobius. Still getting a handle on the changes and the massive refactor and optimizations that have been done.
 
I will certainly look into it make this more of a datapack module to get it out of the core.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 27, 2026, 11:40:12 PM
In this situation, there's a solution: eliminate character replacement altogether. They will only be replaced after the character has sold everything they were selling.

See the first post regarding major changes made.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 28, 2026, 11:06:24 PM
Any chance this system can be done only on datapack using listeners?

As requested I converted the GhostServices to a standalone module.  Even the config is contained in the module folder.
Drop the GhostService folder in the  data/scripts/custom folder.
Edit the GhostServices/config/GhostServices.ini  to enable the shops and wanderers (set to false by default).
Start the server as normal.

Should make much easier to test with other chronicles.

Updated the original post with new link to the repo.
Title: Re: Introducing Ghost Services
Post by: Mobius on April 29, 2026, 01:48:21 AM
As requested I converted the GhostServices to a standalone module.  Even the config is contained in the module folder.
Drop the GhostService folder in the  data/scripts/custom folder.
Edit the GhostServices/config/GhostServices.ini  to enable the shops and wanderers (set to false by default).
Start the server as normal.

Should make much easier to test with other chronicles.

Updated the original post with new link to the repo.
You did more than what I expected :D

Updated so it somewhat follows https://l2jmobius.org/forum/index.php?topic=6556.0 and https://l2jmobius.org/forum/index.php?topic=13048.0 if you are ok with it.
Some variables could be static and not be re-instantiated in methods, but my time was limited.

https://limewire.com/d/yYyXd#fIRNdfEgwf
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 29, 2026, 01:00:30 PM
You did more than what I expected :D

Updated so it somewhat follows https://l2jmobius.org/forum/index.php?topic=6556.0 and https://l2jmobius.org/forum/index.php?topic=13048.0 if you are ok with it.
Some variables could be static and not be re-instantiated in methods, but my time was limited.

https://limewire.com/d/yYyXd#fIRNdfEgwf

Thanks for sharing the programing style. I will work to get the code cleaned up to match this standard.
I am find with the switch from GPLv3 to MIT.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 29, 2026, 05:16:13 PM
Fixed an issue where the shops respawn delay was using minutes instead of seconds.
Lots of code cleanup to keep in line for l2JMobius code standards.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on April 29, 2026, 08:03:42 PM
I create a very simple no frills tool for editing the shopitems.xml file.
See the link in the original post.

From the ReadMe:

Code: [Select]
The ghost shop items are in the GhostServices/data/shopitems.xml.
To make it easier to edit, add and remove items from the list, I created this simple editor.

Run the "L2J_Mobius_Shop_Editor.jar".
Click the "Browse XML" button in the top left.
Select your XML file (Optional: Make a backup first).
The item will be listed in the left panel.
Select an item and the min/max counts, buy low/high, sell low/high and level values will be displayed.
Make you changes and click the Save Changes to XML button.

You can also Add and Remove items using the buttons on the bottom left.

To remove, select and item and click the remove button. You will get a confirmation.

To Add, click the add button.
Enter the item id, Name, and select the category from the dropdown.
This is add it to the list.
Now select the new item and populate the min/max counts, buy low/high, sell low/high and level values.
Click the Save Changes to XML button to update the file.
Title: Re: Introducing Ghost Services
Post by: GuruGel on May 16, 2026, 07:25:41 PM
I noticed that when I try to use the Recall command on traders through the admin panel, they just disappear from the game. How to fix this?
Title: Re: Introducing Ghost Services
Post by: Stayway on May 16, 2026, 08:46:14 PM
Since we use java 25,
For xml editor you should replace it "_" as var to other name e.g
Code: [Select]
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {}

SwingUtilities.invokeLater(() -> new XmlEditor().setVisible(true));
}

And may here too you use
Code: [Select]
private void updateItemFromForm() {
int row = itemTable.getSelectedRow();
if (row != -1) {
Item sel = itemDataList.get(row);

When you have it itemTable.setAutoCreateRowSorter(true); so for it I think you should convert the indice
Code: [Select]
private void updateItemFromForm() {
int viewRow = itemTable.getSelectedRow();

if (viewRow != -1) {
int modelRow = itemTable.convertRowIndexToModel(viewRow);
Item sel = itemDataList.get(modelRow);

sel.mincount = txtMin.getText();
sel.maxcount = txtMax.getText();
sel.buylow = txtBuyL.getText();
sel.buyhigh = txtBuyH.getText();
sel.selllow = txtSellL.getText();
sel.sellhigh = txtSellH.getText();
sel.level = txtLvl.getText();
}
}
is just a idea may works better
Title: Re: Introducing Ghost Services
Post by: mastermind007 on May 26, 2026, 11:23:48 PM
I noticed that when I try to use the Recall command on traders through the admin panel, they just disappear from the game. How to fix this?

You don't. The traders do have a character record in the DB but there are not a full blown character. Add to that, they are running a stripped down client-less method.
If you want to change the random spawn locations. Look in the GhostShopManager.jave file. In the createGhostShopRandom() are the X Min/Max and Y Min/Max that stets up the various box locations. Just take a character and do a /loc command at the opposite corners of whatever box you want to create.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on May 26, 2026, 11:34:31 PM
Since we use java 25,
For xml editor you should replace it "_" as var to other name e.g
Code: [Select]
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {}

SwingUtilities.invokeLater(() -> new XmlEditor().setVisible(true));
}

And may here too you use
Code: [Select]
private void updateItemFromForm() {
int row = itemTable.getSelectedRow();
if (row != -1) {
Item sel = itemDataList.get(row);

When you have it itemTable.setAutoCreateRowSorter(true); so for it I think you should convert the indice
Code: [Select]
private void updateItemFromForm() {
int viewRow = itemTable.getSelectedRow();

if (viewRow != -1) {
int modelRow = itemTable.convertRowIndexToModel(viewRow);
Item sel = itemDataList.get(modelRow);

sel.mincount = txtMin.getText();
sel.maxcount = txtMax.getText();
sel.buylow = txtBuyL.getText();
sel.buyhigh = txtBuyH.getText();
sel.selllow = txtSellL.getText();
sel.sellhigh = txtSellH.getText();
sel.level = txtLvl.getText();
}
}
is just a idea may works better

I am sure that would be better. I knocked that out in about 20 minutes  and did not put any polish on it. Had some people ask about an easy way to edit the shops XML and that's what I came up with. Personally I have the data in a SQL database and built the XML output using SQL procedures, but I know not everyone is fluent in SQL.

Thanks for taking the time to write up the suggestion.
Title: Re: Introducing Ghost Services
Post by: mastermind007 on May 26, 2026, 11:39:04 PM
I created a version of the ghost services for Interlude. 
Wish there was a way to add multiple chronicle tags to a post. It there is I did not see it.