Jump to content

Recommended Posts

Posted (edited)

Idle Auto Farm aka giving rewards to AFK players. I also added an option for premium in case u want to give bonus to premium players. I dont suggest u to use the current threadpool timers it was just for tests. Ideally u have to set it to minutes.

 

IdleFarmer.java

IdleFarmManager.java

pastebin pass : maxcheaters

 

Video

Video 2

It's up to u on how to trigger it for the player

 

  //To start the idle farm
  IdleFarmManager.getInstance().addIdleFarmer(activeChar);
  //To stop the idle farm
  IdleFarmManager.getInstance().removeIdleFarmer(activeChar);

 

GameServer.java
+StringUtil.printSection("Idle Engine");
+IdleFarmManager.getInstance().start();  

 

Player.java
+IdleFarmManager.getInstance().removeIdleFarmer(this);
			
World.getInstance().removePlayer(this); // force remove in case of crash during teleport

 

Edited by LordPanic
  • Like 1
  • Thanks 1
Posted

Thanks for your share!

The main concept for idle farm is the exp,sp. Rewards can be added also by some specific conditions, but in anycase it should't be as a main reward (in my pov).

 

About the code structure, it would be easier/correct to use an interface to build your xp/sp/rewards with their name as a tag. Your _itemsList array should be refactored and also your thought about IdleItem class (by naming view policy, im expecting data of an item (id,count) with the name IdleItem), it could be an intintholder instead.

You can completely remove the class Engine and instead, implement the Runnable class at your IdleFarmManager . It would generate the overriden method 'run'.

 

A security thing now, you are correctly checking for join conditions in order to add someone to your idle farm list, but you are not checking anything up on the reward state. Considering the fact that this feature could be used for low rate servers too, they can easily cheat even if the 1st join condition have the peace zone check.. There are numerous of things that players can think about to 'hack' it. They have recall skills, gotolove or 1000 other things that you can't imagine right now. Also, another benefit that you would get by checking some conditions up on the reward state is that some rewards may need some extra conditions to be applied. For example, you can add an +1 A grade enchant to rhand only if the player is 80 level, have A grade weapon equipped with enchant level < 3 and 0 EAW items found on inventory/warehouse. (Just an example). In that case, you need a different check conditions for that.

 

My personal opinion for this code is to create an interface 

package net.sf.l2j.gameserver.custom.idlefarm.rewards;

import java.time.temporal.ValueRange;

import net.sf.l2j.gameserver.enums.ZoneId;
import net.sf.l2j.gameserver.model.actor.Player;
import net.sf.l2j.gameserver.model.holder.IntIntHolder;

/**
 * @author Melron
 */
public interface IIdleFarmReward
{
	public boolean meetTheConditions(Player player);
	public int getChance();
	public IntIntHolder[] getItemRewards();
	public int getExp(int playerLevel);
	public int getSp(int playerLevel);
	
	default boolean levelIsBetween(Player player, int min, int max)
	{
		return ValueRange.of(min, max).isValidValue(player.getStatus().getLevel());
	}
	
	default boolean isInTown(Player player)
	{
		return player.isInsideZone(ZoneId.PEACE);
	}
}

 

 

and then start creating your classes by giving the correct name. Example, NewbiesReward/AdvancedReward. etc. An example for NewbiesReward :

package net.sf.l2j.gameserver.custom.idlefarm.rewards.type;

import net.sf.l2j.commons.random.Rnd;

import net.sf.l2j.gameserver.custom.idlefarm.rewards.IIdleFarmReward;
import net.sf.l2j.gameserver.model.actor.Player;
import net.sf.l2j.gameserver.model.holder.IntIntHolder;

/**
 * @author Melron
 */
public class NewbiesReward implements IIdleFarmReward
{
	private static final int MIN_LEVEL = 45;
	private static final int MAX_LEVEL = 67;
	private static final IntIntHolder[] ITEM_REWARDS = new IntIntHolder[]
	{
		new IntIntHolder(57, 5_000_000),
		new IntIntHolder(57, 2_000_000),
	};
	
	@Override
	public boolean meetTheConditions(Player player)
	{
		return isInTown(player) && levelIsBetween(player, MIN_LEVEL, MAX_LEVEL);
	}
	
	@Override
	public int getChance()
	{
		return 70;
	}
	
	@Override
	public IntIntHolder[] getItemRewards()
	{
		return ITEM_REWARDS;
	}
	
	@Override
	public int getExp(int playerLevel)
	{
		final int baseValue = playerLevel * 1_000_000;
		return Rnd.get(baseValue, baseValue * 2);
	}
	
	@Override
	public int getSp(int playerLevel)
	{
		final int baseValue = playerLevel * 500_000;
		return Rnd.get(baseValue, baseValue * 2);
	}
}

 

for Advanced: 

 

package net.sf.l2j.gameserver.custom.idlefarm.rewards.type;

import net.sf.l2j.commons.random.Rnd;

import net.sf.l2j.gameserver.custom.idlefarm.rewards.IIdleFarmReward;
import net.sf.l2j.gameserver.model.actor.Player;
import net.sf.l2j.gameserver.model.holder.IntIntHolder;

/**
 * @author Melron
 */
public class AdvancedReward implements IIdleFarmReward
{
	private static final int MIN_LEVEL = 76;
	private static final int MAX_LEVEL = 80;
	private static final IntIntHolder[] REWARDS = new IntIntHolder[]
	{
		new IntIntHolder(961, 1), // EWS
		new IntIntHolder(1540, 100), // QHP
	};
	
	@Override
	public boolean meetTheConditions(Player player)
	{
		return isInTown(player) && levelIsBetween(player, MIN_LEVEL, MAX_LEVEL);
	}
	
	@Override
	public int getChance()
	{
		return 2;
	}
	
	@Override
	public IntIntHolder[] getItemRewards()
	{
		return REWARDS;
	}
	
	@Override
	public int getExp(int playerLevel)
	{
		final int baseValue = playerLevel * 5_000_000;
		return Rnd.get(baseValue, baseValue * 2);
	}
	
	@Override
	public int getSp(int playerLevel)
	{
		final int baseValue = playerLevel * 2_500_000;
		return Rnd.get(baseValue, baseValue * 2);
	}
}

 

 

You can specify whatever you need on the classes and play with meetConditions method to allow the player to get some rewards. (The exp,sp calcs are random).

 

If you want 1 condition to be checked after and a chance check and assuming you have set all your rewards on a Set or something simillar,

for (IIdleFarmReward reward : REWARDS)
{
	if (reward.meetTheConditions(player))
	{
		player.getStatus().addExp(reward.getExp(player.getStatus().getLevel()));
		player.getStatus().addSp(reward.getSp(player.getStatus().getLevel()));
		
		for (IntIntHolder rewardHolder : reward.getItemRewards())
			if (Rnd.get(100) <= reward.getChance())
				player.addItem("Idle Farm", rewardHolder.getId(), rewardHolder.getValue(), null, true);
	}
}

 

But i insist that if you edit a bit my way, you can achieve some cool things for them 🙂

  • Thanks 1
Posted (edited)
6 hours ago, melron said:

The main concept for idle farm is the exp,sp. Rewards can be added also by some specific conditions, but in anycase it should't be as a main reward (in my pov).

Hey Melron , how are u ?
Thanks again for ur useful tips as always. Well obviously it's up to ur taste if u want to optimize it or add extra features to it. I dont think that it's mendatory to add xp/sp especially for low rate servers (i dont think ppl will like it). As u said it's ur pov.

 

6 hours ago, melron said:

Your _itemsList array should be refactored and also your thought about IdleItem class (by naming view policy, im expecting data of an item (id,count) with the name IdleItem), it could be an intintholder instead.

Yeap you are right it's an old bad habit of mine working on to fix it, itemList is array not a List ( i rly need to fix this bad habit).

intintHolder for ur case is right since u work with 2 int's.

 

6 hours ago, melron said:

You can completely remove the class Engine and instead, implement the Runnable class at your IdleFarmManager . It would generate the overriden method 'run'.

Yes i know, i made it this way inorder to make it more flexible let's say for example if u want to make this auto farm thing 2-3 times a month obviously u can do it while the class implements Runnable but i wanted to avoid using interrupts. As it's u can start / close it if u dont want to make it run everytime server starts.

//start idle Engine 
IdleFarmManager.getInstance().start();
//stop idle Engine 
IdleFarmManager.getInstance().stop();

 

 

6 hours ago, melron said:

A security thing now, you are correctly checking for join conditions in order to add someone to your idle farm list, but you are not checking anything up on the reward state. Considering the fact that this feature could be used for low rate servers too, they can easily cheat even if the 1st join condition have the peace zone check.. There are numerous of things that players can think about to 'hack' it. They have recall skills, gotolove or 1000 other things that you can't imagine right now. Also, another benefit that you would get by checking some conditions up on the reward state is that some rewards may need some extra conditions to be applied.

I might be wrong for this one but we make the same checks beside the fact u use interface to achieve it. Or u do sth else that u dont show on the code's.

//Melrons example
@Override
public boolean meetTheConditions(Player player)
{
  return isInTown(player) && levelIsBetween(player, MIN_LEVEL, MAX_LEVEL);
}

//LordPanic example
if(getPlayer().getLevel() >= item._plrLvlMin && getPlayer().getLevel() <= item._plrLvlMax && item._chance > Rnd.get(100))

More security conditions needs to be implemented thats for sure.

 

Still i rly appreciate ur help and if im wrong feel free to point it.

 

P.s i prefer the way i create the IdleItems it might not be readable to the eye but everything is there.

Edited by LordPanic
Posted (edited)
30 minutes ago, LordPanic said:

Hey Melron , how are u ?
Thanks again for ur useful tips as always. Well obviously it's up to ur taste if u want to optimize it or add extra features to it. I dont think that it's mendatory to add xp/sp especially for low rate servers (i dont think ppl will like it). As u said it's ur pov.

Im just 'hearing' the phrase "farm" and im expecting exp,sp and some rewards 😛

 

30 minutes ago, LordPanic said:

I might be wrong for this one but we make the same checks beside the fact u use interface to achieve it. Or u do sth else that u dont show on the code's.

//Melrons example
@Override
public boolean meetTheConditions(Player player)
{
  return isInTown(player) && levelIsBetween(player, MIN_LEVEL, MAX_LEVEL);
}

//LordPanic example
if(getPlayer().getLevel() >= item._plrLvlMin && getPlayer().getLevel() <= item._plrLvlMax && item._chance > Rnd.get(100))

More security conditions needs to be implemented thats for sure.

 

Still i rly appreciate ur help and if im wrong feel free to point it.

 

P.s i prefer the way i create the IdleItems it might not be readable to the eye but everything is there.

 

Well, my purpose wasn't to 'change' your check. Its indeed the same result but im using meetTheConditions at every time i need to reward someone and also im using the method for every reward. Check my code again. As i said with an example, you can add multiple checks and conditions for 1 item reward. As it is now, except that there isn't any check on the reward state, but lets say we add it. It should use the 'same' check for all your rewards. With custom classes, you have the ability to create the necessary check for any item

 

 

Edited by melron
Posted
31 minutes ago, melron said:

Well, my purpose wasn't to 'change' your check. Its indeed the same result but im using meetTheConditions at every time i need to reward someone and also im using the method for every reward. Check my code again. As i said with an example, you can add multiple checks and conditions for 1 item reward. As it is now, except that there isn't any check on the reward state, but lets say we add it. It should use the 'same' check for all your rewards. With custom classes, you have the ability to create the necessary check for any item

Noted, it's not that i didnt understand it, it's simply due to the fact when i create a mod (out of interest) after that i simply lose interest and just leave it as it is (except serious issues).

 

If u are ok with it , send me ur discord it would be better to communicate from there. It's always good to hear a second opinion before every mod share i make or maybe a few tips.

Posted
21 minutes ago, LordPanic said:

Noted, it's not that i didnt understand it, it's simply due to the fact when i create a mod (out of interest) after that i simply lose interest and just leave it as it is (except serious issues).

Mods are mods. You share them and then the receiver can change anything. I'm just pointing out some things not for the mod itself, but for a general purpose.

 

22 minutes ago, LordPanic said:

If u are ok with it , send me ur discord it would be better to communicate from there. It's always good to hear a second opinion before every mod share i make or maybe a few tips.

Not a problem at all, melron#5110

  • 5 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



  • Posts

    • [Release] Solo PvP Zone System 🔹 Compatible with: aCis 401+ 📜 Features: ✅ Automatic Exit on Restart: Players are removed from the zone if a restart occurs or logout. ✅ Custom Exit Command: Players can exit the Solo Zone with the voice command .exit. ✅ Teleport NPC Command: new bypass solopvp for gatekeeper. ✅ Random Name Generator: Generates random names. ✅ PvP Flag: The players are flagged within this zone.   xml preview & java code backup code -> https://pastebin.com/974V2p2p   SoloZone.xml <?xml version="1.0" encoding="UTF-8"?> <list> <zone shape="NPoly" minZ="-5200" maxZ="-4680"><!-- Frintezza Solo Zone --> <stat name="name" val="Solo PvP Zone" /> <stat name="locs" val="174244,-89089,-5112;174260,-86881,-5112;173184,-88090,-5112;175309,-88018,-5112;174231,-88019,-5112;175136,-88828,-5104;174962,-87025,-5104;173149,-87142,-5104;173470,-88908,-5112" /> <stat name="restrictedClasses" val="15,16,97" /> <node x="172031" y="-90127"/> <node x="176428" y="-90089"/> <node x="176428" y="-74051"/> <node x="172057" y="-74108"/> </zone> </list> SoloZone Code: diff --git a/java/net/sf/l2j/gameserver/taskmanager/SoloZoneTaskManager.java b/java/net/sf/l2j/gameserver/taskmanager/SoloZoneTaskManager.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/taskmanager/SoloZoneTaskManager.java @@ -0,0 +1,98 @@ +package net.sf.l2j.gameserver.taskmanager; + +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.logging.Logger; + +import net.sf.l2j.commons.random.Rnd; + +import net.sf.l2j.gameserver.data.manager.ZoneManager; +import net.sf.l2j.gameserver.enums.ZoneId; +import net.sf.l2j.gameserver.handler.voicecommandhandlers.VoiceExitSoloZone; +import net.sf.l2j.gameserver.model.World; +import net.sf.l2j.gameserver.model.actor.Player; +import net.sf.l2j.gameserver.model.location.Location; +import net.sf.l2j.gameserver.model.zone.type.SoloZone; + + +/** + * @author MarGaZeaS + */ +public class SoloZoneTaskManager implements Runnable { + + private static final Location EXIT_LOCATION = VoiceExitSoloZone.getExitLocation(); // Λαμβάνουμε την έξοδο από το VoiceExitSoloZone + + @Override + public void run() + { + // Διασχίζουμε όλους τους παίκτες του κόσμου + for (Player player : World.getInstance().getPlayers()) + { + // Ελέγχουμε αν ο παίκτης είναι στο SoloZone + if (player.isInsideZone(ZoneId.SOLO)) + { + // Μεταφέρουμε τον παίκτη στην έξοδο + player.teleportTo(EXIT_LOCATION.getX(), EXIT_LOCATION.getY(), EXIT_LOCATION.getZ(), 0); + player.sendMessage("The server is restarting, you have been moved out of the Solo Zone."); + } + } + } + + private int _id; + + private static final Logger _log = Logger.getLogger(SoloZoneTaskManager.class.getName()); + private static final ArrayList<String> _rndNames = new ArrayList<>(); + private static final int RANDOM_NAMES = 500; + private static final String CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private int _playersInSoloZone = 0; + + public int getPlayersInside() { + return _playersInSoloZone; + } + + public void setPlayersInside(int val) { + _playersInSoloZone = val; + } + + public SoloZoneTaskManager() { + _log.info("Solo Zone System: Loading..."); + for (int i = 0; i < RANDOM_NAMES; i++) { + String name = generateName(); + _rndNames.add(name); + _log.info("Generated name: " + name); + } + _log.info("Solo Zone System: Loaded " + _rndNames.size() + " names."); + } + + public String getAName() { + if (_rndNames.isEmpty()) { + _log.warning("SoloZoneManager: No random names available."); + return "Unknown"; + } + return _rndNames.get(Rnd.get(5, RANDOM_NAMES - 5)); + } + + private static String generateName() { + SecureRandom rnd = new SecureRandom(); + StringBuilder sb = new StringBuilder(15); + for (int i = 0; i < 15; i++) { + sb.append(CHARS.charAt(rnd.nextInt(CHARS.length()))); + } + return sb.toString(); + } + + public int getZoneId() + { + return _id; + } + + public final static SoloZone getCurrentZone() { + return ZoneManager.getInstance().getAllZones(SoloZone.class) + .stream() + .findFirst() // Επιστρέφει την πρώτη SoloZone (αν υπάρχει μόνο μία) + .orElse(null); + } + + public static SoloZoneTaskManager getInstance() { + return SingletonHolder._instance; + } + + private static class SingletonHolder { + private static final SoloZoneTaskManager _instance = new SoloZoneTaskManager(); + } +} diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/taskmanager/PvpFlagTaskManager.java b/aCis_gameserver/java/net/sf/l2j/gameserver/taskmanager/PvpFlagTaskManager.java index a707ce5..d247e2e 100644 --- a/aCis_gameserver/java/net/sf/l2j/gameserver/taskmanager/PvpFlagTaskManager.java final Player player = entry.getKey(); final long timeLeft = entry.getValue(); + if(player.isInsideZone(ZoneId.SOLO)) + continue; if(player.isInsideZone(ZoneId.BOSS)) continue; // Time is running out, clear PvP flag and remove from list. if (currentTime > timeLeft) diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestCharacterCreate.java b/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestCharacterCreate.java index a707ce5..d247e2e 100644 +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestCharacterCreate.java if (Config.ALLOW_FISH_CHAMPIONSHIP) FishingChampionshipManager.getInstance(); + if (Config.ENABLE_STARTUP) + StartupManager.getInstance(); diff --git a/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminMaintenance.java b/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminMaintenance.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminMaintenance.java if (!st.hasMoreTokens()) { sendHtmlForm(player); return; } try { switch (st.nextToken()) { case "shutdown": + SoloZoneTaskManager exitTask = new SoloZoneTaskManager(); + ThreadPool.schedule(exitTask, 0); Shutdown.getInstance().startShutdown(player, null, Integer.parseInt(st.nextToken()), false); break; case "restart": + exitTask = new SoloZoneTaskManager(); + ThreadPool.schedule(exitTask, 0); Shutdown.getInstance().startShutdown(player, null, Integer.parseInt(st.nextToken()), true); break; case "abort": Shutdown.getInstance().abort(player); break; diff --git a/java/net/sf/l2j/gameserver/handler/voicecommandhandlers/VoiceExitSoloZone.java b/java/net/sf/l2j/gameserver/handler/voicecommandhandlers/VoiceExitSoloZone.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/handler/voicecommandhandlers/VoiceExitSoloZone.java +package net.sf.l2j.gameserver.handler.voicecommandhandlers; + +import net.sf.l2j.commons.pool.ThreadPool; + +import net.sf.l2j.gameserver.enums.ZoneId; +import net.sf.l2j.gameserver.handler.IVoiceCommandHandler; +import net.sf.l2j.gameserver.model.actor.Player; +import net.sf.l2j.gameserver.model.location.Location; +import net.sf.l2j.gameserver.network.serverpackets.MagicSkillUse; + +/** + * Handles the voice command for exiting the Solo Zone with delay and effects. + * + * @author MarGaZeaS + */ +public class VoiceExitSoloZone implements IVoiceCommandHandler +{ + private static final String[] VOICE_COMMANDS = + { + "exit" + }; + + // Default location to teleport players when exiting the Solo Zone + private static final Location EXIT_LOCATION = new Location(81318, 148064, -3464); // Replace with your desired coordinates + + // Προσθήκη της μεθόδου για να πάρουμε την τοποθεσία εξόδου + public static Location getExitLocation() { + return EXIT_LOCATION; + } + + @Override + public void useVoiceCommand(Player player, String command) + { + if (command.equalsIgnoreCase("exit")) + { + if (!player.isInsideZone(ZoneId.SOLO)) + { + player.sendMessage("You are not inside the Solo Zone."); + return; + } + + // Notify the player about the delay + player.sendMessage("You will be teleported out of the Solo Zone in 2 seconds."); + + // Cast skill effect (Skill ID: 2100, Level: 1) + player.broadcastPacket(new MagicSkillUse(player, player, 2100, 1, 2000, 0)); + + // Schedule the teleportation after a 2-second delay + ThreadPool.schedule(() -> { + // Teleport the player to the designated exit location + player.teleportTo(EXIT_LOCATION.getX(), EXIT_LOCATION.getY(), EXIT_LOCATION.getZ(), 0); + + // Inform the player + player.sendMessage("You have exited the Solo Zone."); + }, 2000); // Delay in milliseconds (2000ms = 2 seconds) + } + } + + @Override + public String[] getVoiceCommandList() + { + return VOICE_COMMANDS; + } +} diff --git a/java/net/sf/l2j/gameserver/handler/VoiceCommandHandler.java b/java/net/sf/l2j/gameserver/handler/VoiceCommandHandler.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/handler/VoiceCommandHandler.java public class VoiceCommandHandler { private final Map<String, IVoiceCommandHandler> _entries = new HashMap<>(); protected VoiceCommandHandler() { ............ ............ + registerHandler(new VoiceExitSoloZone()); } public void registerHandler(IVoiceCommandHandler handler) { for (String command : handler.getVoiceCommandList()) _entries.put(command, handler); } diff --git a/java/net/sf/l2j/gameserver/model/actor/Npc.java b/java/net/sf/l2j/gameserver/model/actor/Npc.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/model/actor/Npc.java else if (command.startsWith("Chat")) { int val = 0; try { val = Integer.parseInt(command.substring(5)); } catch (final IndexOutOfBoundsException ioobe) { } catch (final NumberFormatException nfe) { } showChatWindow(player, val); + ) + else if (command.startsWith("solopvp")) + { + SoloZoneTaskManager.getInstance(); + player.teleportTo(SoloZoneTaskManager.getCurrentZone().getLoc(), 25); + } else if (command.startsWith("Link")) { final String path = command.substring(5).trim(); if (path.indexOf("..") != -1) return; final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); html.setFile("data/html/" + path); html.replace("%objectId%", getObjectId()); player.sendPacket(html); } diff --git a/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java b/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestartPoint.java // Fixed. - else if (_requestType == 4) - { - if (!player.isGM() && !player.isFestivalParticipant()) - return; - - loc = player.getPosition(); - } + if (_requestType == 4) + { + // Έλεγχος αν ο παίκτης δεν είναι GM, δεν είναι μέρος του φεστιβάλ και δεν είναι στην Solo Zone + if (!player.isGM() && !player.isFestivalParticipant() && !player.isInsideZone(ZoneId.SOLO)) + { + return; + } + + SoloZoneTaskManager.getInstance(); + SoloZone currentZone = SoloZoneTaskManager.getCurrentZone(); + if (currentZone != null && currentZone.getLoc() != null) + { + // Αν υπάρχει ζώνη και οι τοποθεσίες δεν είναι κενές, χρησιμοποιούμε τυχαία τοποθεσία από την ζώνη + loc = currentZone.getLoc(); + } else + { + // Διαφορετικά, κάνουμε respawn στην τρέχουσα θέση του παίκτη + loc = player.getPosition(); + } + } diff --git a/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestart.java b/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestart.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/network/clientpackets/RequestRestart.java if (player.isFestivalParticipant() && FestivalOfDarknessManager.getInstance().isFestivalInitialized()) { player.sendPacket(SystemMessageId.NO_RESTART_HERE); sendPacket(RestartResponse.valueOf(false)); return; } + if (player.isInsideZone(ZoneId.SOLO)) + { + player.sendMessage("You cannot restart your character while in Solo Zone. Use .exit to leave"); + player.setFakeName(null); + sendPacket(RestartResponse.valueOf(false)); + return; + } player.removeFromBossZone(); diff --git a/java/net/sf/l2j/gameserver/network/clientpackets/Logout.java b/java/net/sf/l2j/gameserver/network/clientpackets/Logout.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/network/clientpackets/Logout.java player.removeFromBossZone(); player.logout(true); } } + + if (player.isInsideZone(ZoneId.SOLO)) + { + player.sendMessage("You cannot logout or restart your character while in Solo Zone. Use .exit to leave"); + player.setFakeName(null); + player.sendPacket(ActionFailed.STATIC_PACKET); + return; + } + player.removeFromBossZone(); player.logout(true); } } diff --git a/java/net/sf/l2j/gameserver/model/zone/type/SoloZone.java b/java/net/sf/l2j/gameserver/model/zone/type/SoloZone.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/model/zone/type/SoloZone.java +package net.sf.l2j.gameserver.model.zone.type; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import net.sf.l2j.commons.random.Rnd; + +import net.sf.l2j.Config; +import net.sf.l2j.gameserver.enums.MessageType; +import net.sf.l2j.gameserver.enums.ZoneId; +import net.sf.l2j.gameserver.handler.voicecommandhandlers.VoiceExitSoloZone; +import net.sf.l2j.gameserver.model.World; +import net.sf.l2j.gameserver.model.actor.Creature; +import net.sf.l2j.gameserver.model.actor.Player; +import net.sf.l2j.gameserver.model.location.Location; +import net.sf.l2j.gameserver.model.zone.type.subtype.ZoneType; +import net.sf.l2j.gameserver.network.SystemMessageId; +import net.sf.l2j.gameserver.network.serverpackets.EtcStatusUpdate; +import net.sf.l2j.gameserver.taskmanager.PvpFlagTaskManager; +import net.sf.l2j.gameserver.taskmanager.SoloZoneTaskManager; + +/** + * @author MarGaZeaS + * + */ +public class SoloZone extends ZoneType +{ + private String _name; + private List<Location> _locations = new ArrayList<>(); + + public SoloZone(int id) + { + super(id); + } + + @Override + public void setParameter(String name, String value) + { + if (name.equals("name")) + _name = value; + else if (name.equals("locs")) + { + for (String locs : value.split(";")) + { + String[] coordinates = locs.split(","); + if (coordinates.length == 3) + { + int x = Integer.parseInt(coordinates[0]); + int y = Integer.parseInt(coordinates[1]); + int z = Integer.parseInt(coordinates[2]); + _locations.add(new Location(x, y, z)); + } + else + { + LOGGER.warn("Invalid location format: " + locs); + } + } + } + } + + + @Override + protected void onEnter(Creature character) + { + if (character instanceof Player) + { + final Player player = (Player) character; + + if ((player.getClassId().getId() == 15 || player.getClassId().getId() == 16 || player.getClassId().getId() == 97)) + { + Location respawnLocation = VoiceExitSoloZone.getExitLocation(); + player.instantTeleportTo(respawnLocation, 20); + player.sendMessage("Your class is not allowed in this zone."); + return; + } + + String randomName = SoloZoneTaskManager.getInstance().getAName(); + if (randomName == null || randomName.isEmpty() || !isValidName(randomName)) + { + randomName = generateRandomName(); + } + if (isNameAlreadyTaken(randomName)) + { + randomName = generateRandomName(); + } + player.setFakeName(randomName); + player.sendMessage("Welcome to the Solo Zone, your random name is: " + randomName); + player.sendPacket(SystemMessageId.ENTERED_COMBAT_ZONE); + character.setInsideZone(ZoneId.SOLO, true); + character.setInsideZone(ZoneId.NO_STORE, true); + character.setInsideZone(ZoneId.NO_SUMMON_FRIEND, true); + + if (player.getParty() != null) + { + player.getParty().removePartyMember(player, MessageType.DISCONNECTED); + } + + if (player.getPvpFlag() > 0) + PvpFlagTaskManager.getInstance().remove(player, true); + + player.updatePvPStatus(); + player.broadcastUserInfo(); + } + } + + private static boolean isValidName(String name) { + return name.matches("[a-zA-Z0-9_]+"); + } + + private static String generateRandomName() { + Random rand = new Random(); + int nameLength = rand.nextInt(12) + 4; + StringBuilder nameBuilder = new StringBuilder(); + + for (int i = 0; i < nameLength; i++) { + char randomChar = (char) (rand.nextInt(26) + 'a'); + nameBuilder.append(randomChar); + } + + return nameBuilder.toString(); + } + + private static boolean isNameAlreadyTaken(String name) { + return World.getInstance().getPlayers().stream().anyMatch(player -> player.getFakeName().equals(name)); + } + + @Override + protected void onExit(Creature character) + { + character.setInsideZone(ZoneId.SOLO, false); // Solo zone + character.setInsideZone(ZoneId.NO_STORE, false); // Allow making a store + character.setInsideZone(ZoneId.NO_SUMMON_FRIEND, false); // Allow summon + + if (character instanceof Player) + { + final Player player = (Player) character; + + if (player.getFakeName() != null) + { + player.setFakeName(null); + } + + player.sendPacket(SystemMessageId.LEFT_COMBAT_ZONE); + { + if(!player.isInObserverMode() && player.getPvpFlag() > 0) + PvpFlagTaskManager.getInstance().add(player, Config.PVP_NORMAL_TIME); + + player.sendPacket(new EtcStatusUpdate(player)); + player.broadcastUserInfo(); + } + } + } + + public String getName() + { + return _name; + } + + public Location getLoc() + { + if (_locations.isEmpty()) + { + return null; // Αν η λίστα είναι κενή, επιστρέφουμε null + } + return _locations.get(Rnd.get(0, _locations.size() - 1)); // Επιλέγουμε τυχαία τοποθεσία + } +} diff --git a/java/net/sf/l2j/gameserver/GameServer.java b/java/net/sf/l2j/gameserver/GameServer.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/GameServer.java if (Config.ALLOW_FISH_CHAMPIONSHIP) FishingChampionshipManager.getInstance(); + StringUtil.printSection("Custom Features"); + SoloZoneTaskManager.getInstance(); StringUtil.printSection("Handlers"); LOGGER.info("Loaded {} admin command handlers.", AdminCommandHandler.getInstance().size()); diff --git a/java/net/sf/l2j/gameserver/GameServer.java b/java/net/sf/l2j/gameserver/Shutdown.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/Shutdown.java // disconnect players try { disconnectAllPlayers(); LOGGER.info("All players have been disconnected."); } catch (Exception e) { // Silent catch. } + // Restore real names for players in SoloZone + restoreRealNamesInSoloZone(); // stop all threadpolls ThreadPool.shutdown(); try { LoginServerThread.getInstance().interrupt(); } catch (Exception e) { // Silent catch. } // avoids new players from logging in if (_secondsShut <= 60 && LoginServerThread.getInstance().getServerType() != ServerType.DOWN) LoginServerThread.getInstance().setServerType(ServerType.DOWN); _secondsShut--; Thread.sleep(1000); } } catch (InterruptedException e) { } } + // This method restores the real names of players in SoloZone + private static void restoreRealNamesInSoloZone() + { + for (Player player : World.getInstance().getPlayers()) + { + // Check if player is inside the SoloZone + if (player.isInsideZone(ZoneId.SOLO)) + { + // Restore the real name by removing the fake name + if (player.getFakeName() != null) + { + player.setFakeName(null); // Restore the real name + LOGGER.info("Player {}'s fake name has been removed and real name restored.", player.getName()); + } + } + } + } private static void sendServerQuit(int seconds) { World.toAllOnlinePlayers(SystemMessage.getSystemMessage(SystemMessageId.THE_SERVER_WILL_BE_COMING_DOWN_IN_S1_SECONDS).addNumber(seconds)); } diff --git a/java/net/sf/l2j/gameserver/enums/ZoneId.java b/java/net/sf/l2j/gameserver/enums/ZoneId.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/enums/ZoneId.java public enum ZoneId { PVP(0), PEACE(1), SIEGE(2), MOTHER_TREE(3), CLAN_HALL(4), NO_LANDING(5), WATER(6), JAIL(7), MONSTER_TRACK(8), CASTLE(9), SWAMP(10), NO_SUMMON_FRIEND(11), NO_STORE(12), TOWN(13), HQ(14), DANGER_AREA(15), CAST_ON_ARTIFACT(16), NO_RESTART(17), SCRIPT(18), - BOSS(19), + BOSS(19), + SOLO(20); private final int _id; private ZoneId(int id) { _id = id; } diff --git a/java/net/sf/l2j/gameserver/network/serverpackets/Die.java b/java/net/sf/l2j/gameserver/network/serverpackets/Die.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/network/serverpackets/Die.java if (creature instanceof Player) { Player player = (Player) creature; - _allowFixedRes = player.getAccessLevel().allowFixedRes(); + _allowFixedRes = player.getAccessLevel().allowFixedRes() || player.isInsideZone(ZoneId.SOLO); _clan = player.getClan(); } diff --git a/java/net/sf/l2j/gameserver/model/actor/Player.java b/java/net/sf/l2j/gameserver//model/actor/Player.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/network/clientpackets/EnterWorld.java // Attacker or spectator logging into a siege zone will be ported at town. if (player.isInsideZone(ZoneId.SIEGE) && player.getSiegeState() < 2) player.teleportTo(TeleportType.TOWN); + if (player.isInsideZone(ZoneId.SOLO)) + { + ThreadPool.schedule(() -> { + Location exitLocation = VoiceExitSoloZone.getExitLocation(); + + if (exitLocation != null) + { + player.teleportTo(exitLocation.getX(), exitLocation.getY(), exitLocation.getZ(), 0); + player.sendMessage("You have been moved to the exit of the SoloZone."); + } + }, 5000); // 5000 milliseconds (5sec) + } diff --git a/java/net/sf/l2j/gameserver/model/actor/Player.java b/java/net/sf/l2j/gameserver/model/actor/Player.java new file mode 100644 index 0000000..6b7ef6f --- /dev/null +++ a/java/net/sf/l2j/gameserver/model/actor/Player.java @Override public void doRevive() { super.doRevive(); stopEffects(EffectType.CHARM_OF_COURAGE); sendPacket(new EtcStatusUpdate(this)); getStatus().setCpHpMp(getStatus().getMaxCp(), getStatus().getMaxHp(), getStatus().getMaxMp()); _reviveRequested = 0; _revivePower = 0; if (isMounted()) startFeed(_mountNpcId); + if (isInsideZone(ZoneId.SOLO)) + { + // Give Nobless (1323 ID) + L2Skill no = SkillTable.getInstance().getInfo(1323, 1); + no.getEffects(this, this); + sendMessage("You have received the Nobless status in the Solo Zone."); + } + }   If anyone thinks the code is wrong, please make an update and upload it here so I can update the post. A part was edited with chatgpt
    • Always remember, when you buy files, just compare with my files that I publish for free. and you will know that you are being ripped off. Greetings to all community!!! 🙂
    • Thank you for sharing. You are a capable and skilled person. Thank you again for your selfless dedication, Guytis🫡
    • he kept his promise! i think it's a good idea to unban his old account. he shares files with the community and could help both new and veteran l2off users! good job, Guytis!
  • Topics

×
×
  • Create New...