Jump to content

Trance

Global Moderator
  • Posts

    3,917
  • Joined

  • Last visited

  • Days Won

    67
  • Feedback

    0%

Everything posted by Trance

  1. Most L2 developers are not willing to work remotely through Team Viewer or AnyDesk due to the low income and their moody nature. Therefore, it is highly unlikely to find an L2 developer willing to work under these conditions.
  2. You don't need to copy and paste everything. Instead, you can use the same logic to implement it in your Thread Pool Manager. However, I am providing you with the files: https://files.lineage2.gold/threads.zip
  3. Hi, This is a custom implementation of a thread pool management system, designed to efficiently manage the execution of tasks in a multi-threaded environment. Automatically adjusts the size of the thread pools based on the system's CPU load and the number of available cores. This dynamic adjustment ensures that the thread pools are always optimized for the current system load, resulting in efficient resource utilization and improved performance. If the calculated sizes are equal to the current core pool sizes, this means that there's no need to update the pool sizes, as they are already optimized for the current system load. I haven't conducted extensive testing yet; however, the code can be thoroughly tested, and if any issues are encountered, I can make the necessary modifications. Comments were added throughout the code. For testing purposes you can do the following: scheduleAtFixedRate(() -> { try { purge(); updateThreadPoolSizes(); } catch (Exception e) { LOGGER.severe("Error during scheduled ThreadPool update: " + e.getMessage()); } }, 10, 10, TimeUnit.SECONDS); private static void updateThreadPoolSizes() { double systemLoad = getSystemCpuLoad(); int availableCores = Runtime.getRuntime().availableProcessors(); int newScheduledThreadPoolSize = calculateThreadPoolSize(availableCores, systemLoad, true); int newInstantThreadPoolSize = calculateThreadPoolSize(availableCores, systemLoad, false); if (newScheduledThreadPoolSize != scheduledExecutor.getCorePoolSize()) { scheduledExecutor.setCorePoolSize(newScheduledThreadPoolSize); LOGGER.info("Updated scheduled thread pool size to " + newScheduledThreadPoolSize + ", CPU: " + getSystemCpuLoadPercentage()); } else { LOGGER.info("Scheduled thread pool size remains to " + newScheduledThreadPoolSize + ", CPU: " + getSystemCpuLoadPercentage()); } if (newInstantThreadPoolSize != instantExecutor.getCorePoolSize()) { instantExecutor.setCorePoolSize(newInstantThreadPoolSize); LOGGER.info("Updated instant thread pool size to " + newInstantThreadPoolSize + ", CPU: " + getSystemCpuLoadPercentage()); } else { LOGGER.info("Instant thread pool size remains to " + newInstantThreadPoolSize + ", CPU: " + getSystemCpuLoadPercentage()); } } P.S.: This is the lowest it can get: return Math.max(isScheduledPool ? 16 : 8, threadPoolSize); The initial code: * This file is part of the L2Gold Classic project. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package gold.lineage2.commons.threads; import java.lang.management.ManagementFactory; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import com.sun.management.OperatingSystemMXBean; /** * A custom implementation of a thread pool management system, designed to efficiently manage the execution of tasks in a multi-threaded environment. * Automatically adjusts the size of the thread pools based on the system's CPU load and the number of available cores. This dynamic adjustment ensures * that the thread pools are always optimized for the current system load, resulting in efficient resource utilization and improved performance. * @author Trance */ public class ThreadPool { // Logger to log information and errors. private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName()); // Maximum delay to be used to validate the delay. private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2; // ScheduledThreadPoolExecutor for scheduled tasks. private static ScheduledThreadPoolExecutor scheduledExecutor; // ThreadPoolExecutor for instant tasks. private static ThreadPoolExecutor instantExecutor; /** * Initialize the ThreadPool with the appropriate sizes. */ public static void init() { LOGGER.info("ThreadPool: Initialized"); int availableCores = Runtime.getRuntime().availableProcessors(); int scheduledThreadPoolSize = availableCores * 4; int instantThreadPoolSize = availableCores * 2; scheduledExecutor = new ScheduledThreadPoolExecutor(scheduledThreadPoolSize, new PriorityThreadFactory("ScheduledThreadPool", Thread.NORM_PRIORITY), new ThreadPoolExecutor.CallerRunsPolicy()); scheduledExecutor.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl()); scheduledExecutor.prestartAllCoreThreads(); LOGGER.info("Scheduled thread pool size: " + scheduledThreadPoolSize); instantExecutor = new ThreadPoolExecutor(instantThreadPoolSize, Integer.MAX_VALUE, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("ThreadPoolExecutor", Thread.NORM_PRIORITY), new ThreadPoolExecutor.CallerRunsPolicy()); instantExecutor.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl()); instantExecutor.prestartAllCoreThreads(); LOGGER.info("Instant thread pool size: " + instantThreadPoolSize); scheduleAtFixedRate(() -> { try { purge(); updateThreadPoolSizes(); } catch (Exception e) { LOGGER.severe("Error during scheduled ThreadPool update: " + e.getMessage()); } }, 5, 5, TimeUnit.MINUTES); } /** * Purge both thread pools. */ public static void purge() { scheduledExecutor.purge(); instantExecutor.purge(); } /** * Validate the delay ensuring it's within acceptable bounds. * @param delay The delay value to be validated. * @param timeUnit The time unit of the delay. * @return A validated delay value. */ private static long validate(long delay, TimeUnit timeUnit) { long delayInMilliseconds = timeUnit.toMillis(delay); long validatedDelay = Math.max(0, Math.min(MAX_DELAY, delayInMilliseconds)); if (delayInMilliseconds > validatedDelay) { return -1; } return timeUnit.convert(delayInMilliseconds, TimeUnit.MILLISECONDS); } /** * Schedule a Runnable with a specific delay. * @param r The Runnable to be scheduled. * @param delay The delay before the Runnable is executed, in milliseconds. * @return A ScheduledFuture representing the pending result of the task. */ public static ScheduledFuture<?> schedule(Runnable r, long delay) { return schedule(r, delay, TimeUnit.MILLISECONDS); } /** * Schedule a Runnable with a specific delay and TimeUnit. * @param r The Runnable to be scheduled. * @param delay The delay before the Runnable is executed. * @param timeUnit The time unit of the delay. * @return A ScheduledFuture representing the result of the scheduling. */ private static ScheduledFuture<?> schedule(Runnable r, long delay, TimeUnit timeUnit) { delay = validate(delay, timeUnit); if (delay == -1) { return null; } return scheduledExecutor.schedule(new RunnableWrapper(r), delay, timeUnit); } /** * Schedule a Runnable at a fixed rate with an initial delay. * @param r The Runnable to be scheduled. * @param initial The initial delay before the Runnable is executed for the first time. * @param delay The delay between the execution of the Runnable in subsequent runs. * @return A ScheduledFuture representing the result of the scheduling. */ public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long initial, long delay) { return scheduleAtFixedRate(r, initial, delay, TimeUnit.MILLISECONDS); } /** * Schedule a Runnable at a fixed rate with an initial delay and TimeUnit. * @param r The Runnable to be scheduled. * @param initial The initial delay before the Runnable is executed for the first time. * @param delay The delay between the execution of the Runnable in subsequent runs. * @param timeUnit The time unit of the delay. * @return A ScheduledFuture representing the result of the scheduling. */ private static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long initial, long delay, TimeUnit timeUnit) { initial = validate(initial, timeUnit); if (initial == -1) { return null; } delay = validate(delay, timeUnit); if (delay == -1) { return scheduledExecutor.schedule(new RunnableWrapper(r), initial, timeUnit); } return scheduledExecutor.scheduleAtFixedRate(new RunnableWrapper(r), initial, delay, timeUnit); } /** * Execute a Runnable instantly. * @param r The Runnable to be executed. */ public static void execute(Runnable r) { instantExecutor.execute(r); } /** * Shut down both thread pools. * @throws InterruptedException If the shutdown process is interrupted. */ public static void shutdown() throws InterruptedException { try { scheduledExecutor.shutdown(); if (!scheduledExecutor.awaitTermination(10, TimeUnit.SECONDS)) { scheduledExecutor.shutdownNow(); } } finally { instantExecutor.shutdown(); if (!instantExecutor.awaitTermination(1, TimeUnit.MINUTES)) { instantExecutor.shutdownNow(); } } } /** * Update the thread pool sizes based on system load. */ private static void updateThreadPoolSizes() { double systemLoad = getSystemCpuLoad(); int availableCores = Runtime.getRuntime().availableProcessors(); int newScheduledThreadPoolSize = calculateThreadPoolSize(availableCores, systemLoad, true); int newInstantThreadPoolSize = calculateThreadPoolSize(availableCores, systemLoad, false); if (newScheduledThreadPoolSize != scheduledExecutor.getCorePoolSize()) { scheduledExecutor.setCorePoolSize(newScheduledThreadPoolSize); LOGGER.info("Updated scheduled thread pool size to " + newScheduledThreadPoolSize + ", CPU: " + getSystemCpuLoadPercentage()); } if (newInstantThreadPoolSize != instantExecutor.getCorePoolSize()) { instantExecutor.setCorePoolSize(newInstantThreadPoolSize); LOGGER.info("Updated instant thread pool size to " + newInstantThreadPoolSize + ", CPU: " + getSystemCpuLoadPercentage()); } } /** * Calculate the thread pool size based on available cores, system load, and whether it's a scheduled pool. * @param availableCores The number of available processor cores. * @param systemLoad The current system load. * @param isScheduledPool A boolean indicating if the pool is a scheduled thread pool or not. * @return The calculated thread pool size. */ private static int calculateThreadPoolSize(int availableCores, double systemLoad, boolean isScheduledPool) { double factor; if (systemLoad <= 0.4) { factor = isScheduledPool ? 4 : 2; } else if (systemLoad <= 0.6) { factor = isScheduledPool ? 3 : 1.5; } else if (systemLoad <= 0.8) { factor = isScheduledPool ? 2 : 1; } else { factor = 0.5; } int threadPoolSize = (int) Math.round(availableCores * factor); return Math.max(isScheduledPool ? 16 : 8, threadPoolSize); } /** * Get the system CPU load. * @return A double value representing the system CPU load. */ @SuppressWarnings("deprecation") private static double getSystemCpuLoad() { OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class); return osBean.getSystemCpuLoad(); } /** * Get the system CPU load as a percentage. * @return A string representing the system CPU load percentage. */ private static String getSystemCpuLoadPercentage() { double cpuLoad = getSystemCpuLoad(); return String.format("%.2f%%", cpuLoad * 100); } }
  4. Hi, It searches for a suitable location in a circular area around the original spawn location by incrementally increasing the distance from the original point and checking different angles. It then checks if there's a wall or obstruction within safeDistance units from the potential new location. Index: /main/L2GoldClassic/java/gold/lineage2/gameserver/model/Spawn.java =================================================================== --- /main/L2GoldClassic/java/gold/lineage2/gameserver/model/Spawn.java (revision 25) +++ /main/L2GoldClassic/java/gold/lineage2/gameserver/model/Spawn.java (revision 26) @@ -29,4 +29,5 @@ import gold.lineage2.gameserver.data.xml.NpcData; import gold.lineage2.gameserver.geoengine.GeoEngine; +import gold.lineage2.gameserver.geoengine.geodata.GeoStructure; import gold.lineage2.gameserver.instancemanager.DropManager; import gold.lineage2.gameserver.instancemanager.WalkingManager; @@ -407,13 +408,24 @@ } - // Correct Z of monsters. Do not correct Z of flying NPCs. - if (npc.isMonster() && !npc.isFlying()) - { - final int geoZ = GeoEngine.getInstance().getHeight(newlocx, newlocy, newlocz); - // Do not correct Z distances greater than 300. - if (Util.calculateDistance(newlocx, newlocy, newlocz, newlocx, newlocy, geoZ, true, false) < 300) + if (npc.isMonster()) + { + // Correct Z of monsters. Do not correct Z of flying NPCs. + if (!npc.isFlying()) { - newlocz = geoZ; + final int geoZ = GeoEngine.getInstance().getHeight(newlocx, newlocy, newlocz); + // Do not correct Z distances greater than 300. + if (Util.calculateDistance(newlocx, newlocy, newlocz, newlocx, newlocy, geoZ, true, false) < 300) + { + newlocz = geoZ; + } } + + // Find a safe spawn location + Location safeSpawnLocation = findSafeSpawnLocation(newlocx, newlocy, newlocz, 200); + + // Update the spawn location + newlocx = safeSpawnLocation.getX(); + newlocy = safeSpawnLocation.getY(); + newlocz = safeSpawnLocation.getZ(); } @@ -475,4 +487,53 @@ return npc; + } + + /** + * Finds a safe spawn location at least 'safeDistance' units away from the nearest wall. + * + * @param x the original x-coordinate of the spawn location + * @param y the original y-coordinate of the spawn location + * @param z the original z-coordinate of the spawn location + * @param safeDistance the minimum distance from the nearest wall for the new spawn location + * @return a new Location object containing the safe spawn location + */ + private Location findSafeSpawnLocation(int x, int y, int z, int safeDistance) + { + Location newLocation = new Location(x, y, z); + GeoEngine geoEngine = GeoEngine.getInstance(); + + // Search for a safe spawn location in a circular area around the original spawn location + for (int radius = safeDistance; radius < 5000; radius += safeDistance) + { + for (int angle = 0; angle < 360; angle += 45) + { + int newX = x + (int) (radius * Math.cos(Math.toRadians(angle))); + int newY = y + (int) (radius * Math.sin(Math.toRadians(angle))); + int newZ = geoEngine.getHeight(newX, newY, z); + + boolean canSeeWall = false; + for (int checkDistance = 0; checkDistance <= safeDistance; checkDistance += GeoStructure.CELL_IGNORE_HEIGHT) + { + int checkX = newX + (int) (checkDistance * Math.cos(Math.toRadians(angle))); + int checkY = newY + (int) (checkDistance * Math.sin(Math.toRadians(angle))); + int checkZ = geoEngine.getHeight(checkX, checkY, newZ); + + if (geoEngine.hasGeoPos(checkX, checkY) && !geoEngine.canMoveToTarget(newX, newY, newZ, checkX, checkY, checkZ, null)) + { + canSeeWall = true; + break; + } + } + + if (!canSeeWall) + { + newLocation.setXYZ(newX, newY, newZ); + return newLocation; + } + } + } + + LOGGER.warning("Could not find a safe spawn location for NPC " + _template.getId() + ". Using original location."); + return newLocation; }
      • 2
      • Like
  5. Hi, Admin command, scans a specified range around the admin. It gathers about players within the specified range, including their clan names, party leaders, and class names. Counting all players around can be a helpful task for admins. You could limit the scanning to players within the same region or zone as the admin - this would reduce the number of players to be processed and improve performance. Index: /main/L2GoldClassic/dist/game/config/AdminCommands.xml =================================================================== --- /main/L2GoldClassic/dist/game/config/AdminCommands.xml (revision 26) +++ /main/L2GoldClassic/dist/game/config/AdminCommands.xml (revision 27) @@ -626,4 +626,5 @@ <admin command="admin_scan" accessLevel="100" /> <admin command="admin_deleteNpcByObjectId" accessLevel="100" confirmDlg="true" /> + <admin command="admin_scan_players" accessLevel="50" /> <!-- ADMIN SERVERINFO --> Index: /main/L2GoldClassic/dist/game/data/scripts/handlers/MasterHandler.java =================================================================== --- /main/L2GoldClassic/dist/game/data/scripts/handlers/MasterHandler.java (revision 26) +++ /main/L2GoldClassic/dist/game/data/scripts/handlers/MasterHandler.java (revision 27) @@ -124,4 +124,5 @@ import handlers.admincommandhandlers.AdminRide; import handlers.admincommandhandlers.AdminScan; +import handlers.admincommandhandlers.AdminScanPlayers; import handlers.admincommandhandlers.AdminServerInfo; import handlers.admincommandhandlers.AdminShop; @@ -463,4 +464,5 @@ AdminRide.class, AdminScan.class, + AdminScanPlayers.class, AdminServerInfo.class, AdminShop.class, Index: /main/L2GoldClassic/dist/game/data/scripts/handlers/admincommandhandlers/AdminScanPlayers.java =================================================================== --- /main/L2GoldClassic/dist/game/data/scripts/handlers/admincommandhandlers/AdminScanPlayers.java (revision 27) +++ /main/L2GoldClassic/dist/game/data/scripts/handlers/admincommandhandlers/AdminScanPlayers.java (revision 27) @@ -0,0 +1,134 @@ +/* + * This file is part of the L2Gold Classic project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package handlers.admincommandhandlers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import gold.lineage2.gameserver.data.sql.ClanTable; +import gold.lineage2.gameserver.handler.IAdminCommandHandler; +import gold.lineage2.gameserver.model.Location; +import gold.lineage2.gameserver.model.World; +import gold.lineage2.gameserver.model.actor.instance.PlayerInstance; +import gold.lineage2.gameserver.model.clan.Clan; +import gold.lineage2.gameserver.util.Util; + +/** + * @author Trance + */ +public class AdminScanPlayers implements IAdminCommandHandler +{ + private static final String[] ADMIN_COMMANDS = + { + "admin_scan_players" + }; + + @Override + public boolean useAdminCommand(String command, PlayerInstance player) + { + if (command.startsWith("admin_scan_players")) + { + String[] args = command.split(" "); + if (args.length > 1) + { + String rangeStr = args[1]; + scanPlayers(player, rangeStr); + } + else + { + player.sendMessage("Usage: //admin_scan_players <range>"); + } + } + return true; + } + + private void scanPlayers(PlayerInstance player, String rangeStr) + { + // Parse the range from the command arguments + int range = Util.parseNextInt(rangeStr, 0); + + // Get the player's location + Location playerLocation = player.getLocation(); + + // Initialize data structures for storing the scan results + Map<String, Set<String>> clanMembers = new HashMap<>(); + Map<String, Set<String>> partyMembers = new HashMap<>(); + Map<String, List<String>> classMembers = new HashMap<>(); + + // Iterate through all players in the game world + for (PlayerInstance otherPlayer : World.getInstance().getPlayers()) + { + // Check if the other player is within the specified range of the player + if (playerLocation.distanceTo(otherPlayer.getLocation()) <= range) + { + // Gather data for clans, parties, and classes + String clanName = otherPlayer.getClan() != null ? otherPlayer.getClan().getName() : "No Clan"; + String partyLeaderName = otherPlayer.getParty() != null ? otherPlayer.getParty().getLeader().getName() : null; + String className = otherPlayer.getTemplate().getClassId().name(); + + // Update the player lists for clans, parties, and classes + clanMembers.computeIfAbsent(clanName, k -> new HashSet<>()).add(otherPlayer.getName()); + if (partyLeaderName != null) + { + partyMembers.computeIfAbsent(partyLeaderName, k -> new HashSet<>()).add(otherPlayer.getName()); + } + classMembers.computeIfAbsent(className, k -> new ArrayList<>()).add(otherPlayer.getName()); + } + } + + // Display the scan results to the player + player.sendMessage("Scan Results:"); + player.sendMessage("Range: " + range); + player.sendMessage("Clans (" + clanMembers.size() + "):"); + for (Map.Entry<String, Set<String>> entry : clanMembers.entrySet()) + { + String clanLeaderName = "Unknown"; + Clan clan = ClanTable.getInstance().getClanByName(entry.getKey()); + if (clan != null) + { + PlayerInstance clanLeader = World.getInstance().getPlayer(clan.getLeaderId()); + if (clanLeader != null) + { + clanLeaderName = clanLeader.getName(); + } + } + player.sendMessage(" - " + entry.getKey() + " (Leader: " + clanLeaderName + "): " + entry.getValue().size() + " members"); + player.sendMessage(" Members: " + String.join(", ", entry.getValue())); + } + player.sendMessage("Parties (" + partyMembers.size() + "):"); + for (Map.Entry<String, Set<String>> entry : partyMembers.entrySet()) + { + player.sendMessage(" - Leader: " + entry.getKey() + ", " + entry.getValue().size() + " members"); + player.sendMessage(" Members: " + String.join(", ", entry.getValue())); + } + player.sendMessage("Classes (" + classMembers.size() + "):"); + for (Map.Entry<String, List<String>> entry : classMembers.entrySet()) + { + player.sendMessage(" - " + entry.getKey() + ": " + entry.getValue().size() + " players " + entry.getValue().stream().collect(Collectors.joining(", ", "(", ")"))); + } + } + + @Override + public String[] getAdminCommandList() + { + return ADMIN_COMMANDS; + } +} Index: /main/L2GoldClassic/java/gold/lineage2/gameserver/model/Location.java =================================================================== --- /main/L2GoldClassic/java/gold/lineage2/gameserver/model/Location.java (revision 26) +++ /main/L2GoldClassic/java/gold/lineage2/gameserver/model/Location.java (revision 27) @@ -56,4 +56,17 @@ _z = set.getInt("z"); _heading = set.getInt("heading", 0); + } + + /** + * Calculates the Euclidean distance between two 3D points in the world + * @param other + * @return + */ + public double distanceTo(Location other) + { + int dx = _x - other._x; + int dy = _y - other._y; + int dz = _z - other._z; + return Math.sqrt((dx * dx) + (dy * dy) + (dz * dz)); } Index: /main/L2GoldClassic/java/gold/lineage2/gameserver/util/Util.java =================================================================== --- /main/L2GoldClassic/java/gold/lineage2/gameserver/util/Util.java (revision 26) +++ /main/L2GoldClassic/java/gold/lineage2/gameserver/util/Util.java (revision 27) @@ -266,4 +266,22 @@ /** + * Tries to parse an integer from a string. If it fails, it returns the default value provided. + * @param value + * @param defaultValue + * @return + */ + public static int parseNextInt(String value, int defaultValue) + { + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException e) + { + return defaultValue; + } + } + + /** * @param text - the text to check * @return {@code true} if {@code text} is float, {@code false} otherwise
      • 4
      • Upvote
      • Like
  6. The client side only changes the information and skill category. What you need to change on the server side is the target, skill type, operation type and effect to be Damage Over Time instead of Buff.
  7. I didn't read the whole code: final Player tmp = _player.getClient().getPlayer(); final Party party = tmp.getParty();
  8. Kindly delete this code permanently. I'm not kidding.
  9. It's necessary for you to indicate the pack and the type of client protection you're using. By default, I don't believe the client is capable of supporting that.
  10. Moved to client section.
  11. You don't need: final Player tmp = getClient().getPlayer(); Change: final Party party = tmp.getParty(); To: final Party party = _player.getParty();
  12. stackType=""
  13. Why would you even want to do that?
  14. I have refreshed the GitHub repositories referenced in the main topic to enhance the coherence between the guides.
  15. I've added Webmin Installation Guide - a web-based system administration tool.
  16. @ZOUMHS Please follow the rules: 10) Prices must be displayed (and visible) in public on the main topic.
  17. Topic renamed from L2Gold Classic to L2Gold Essence. We are excited to announce the expansion of our team for the remastered Essence chronicle. Our goal is to adopt a classic, long-lasting playstyle while still maintaining the legacy of the gold-style. With that in mind, we are thrilled to introduce the following members who will help us achieve this vision: Electronica: Our new Community Manager, Electronica brings a wealth of experience in community management to our team. He will represent the community and ensure that player needs and concerns are addressed to the Developer and other staff members. Electronica will also assist with communication and other aspects of the server. Psy: A Game Master with experience in various custom private servers, Psy will help ensure fair gameplay and assist with dispute resolution. He has a deep understanding of the server and its customizations. Techno: Another Game Master with experience in Interlude and all the latest chronicles, Techno brings a passion for gaming to our team. He will work alongside Psy and House to monitor gameplay and help maintain a fair and enjoyable game environment for all players. House: Another Game Master, our seasoned veteran, House, has been part of the World of Lineage community since the official release of Chronical C3 in 2005. As a project manager in real life, he brings his organizational skills and attention to detail to our team. He will work closely with the Game Masters to ensure that everything runs smoothly. AskForMore: Our Community Helper, AskForMore, is a familiar face to many players. He knows the server inside out and is always ready to assist with any questions or concerns. He will continue to be an essential part of our team. We are still looking for one more Game Master and two GM Helpers to join our team. Game Masters will be responsible for monitoring gameplay, resolving disputes, and ensuring a fair and enjoyable game environment. GM Helpers will assist the Game Masters with their duties and help monitor game activity. We are confident that our team of experienced and dedicated individuals will help us achieve our goal of providing a classic and enjoyable gaming experience. We look forward to working together and hope that you'll join us on this journey. Discord: https://discord.gg/zA5KWH8cMc
  18. Please follow the rules: 10) Prices must be displayed (and visible) in public on the main topic.
  19. Please follow the rules: 10) Prices must be displayed (and visible) in public on the main topic.
  20. Please follow the rules: 10) Prices must be displayed (and visible) in public on the main topic.
  21. Thanks for pointing out that I forgot to add the installation of conntrack in my GitHub repository, as it is already integrated into this repository. To install conntrack, you can use the following command: sudo apt-get install conntrack -y it's possible that your system has some restrictions on the maximum allowed values for net.ipv4.tcp_rmem and net.ipv4.tcp_wmem, try to lower the values. It's basically min, default, max - receive buffer space for TCP connections. Which configuration did you try to apply? P.S. I've updated the GitHub repo.
×
×
  • Create New...

AdBlock Extension Detected!

Our website is made possible by displaying online advertisements to our members.

Please disable AdBlock browser extension first, to be able to use our community.

I've Disabled AdBlock