Parse only the zone templates the map's zone tree uses

The world server read all 861 CZONE files; map 108 uses 633. The zone tree
is now read first and the parser loads only the templates it names. A
template with no CZONE file aborts boot from the parser, replacing the
check in loadZones.

The tree no longer carries player-city zones (mbScryer drops them at
export), so isPlayerCity and its skip are gone. An old tree still holding
them aborts boot: template 0 has no CZONE file.

Value changes: none; 228 unused templates are no longer in memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MagicBotandClaude Opus 5.5 committed 2026-10-27 22:11:37 -04:00
1 parent 807a806a0d
commit 3b4a60c739
5 files changed
+44 -32

No files matched your search

+19 -20
View File
@@ -39,6 +39,7 @@ public enum ZoneManager {
ZONEMANAGER; ZONEMANAGER;
public static HashMap<Integer, CZoneTemplate> _zone_templates = new HashMap<>(); public static HashMap<Integer, CZoneTemplate> _zone_templates = new HashMap<>();
public static ArrayList<ZoneTreeEntry> zoneTree;
public static final Set<Zone> macroZones = Collections.newSetFromMap(new ConcurrentHashMap<>()); public static final Set<Zone> macroZones = Collections.newSetFromMap(new ConcurrentHashMap<>());
private static final ConcurrentHashMap<Integer, Zone> zonesByID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD); private static final ConcurrentHashMap<Integer, Zone> zonesByID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD);
@@ -76,37 +77,35 @@ public enum ZoneManager {
Logger.info("Zone templates: " + _zone_templates.size()); Logger.info("Zone templates: " + _zone_templates.size());
} }
// Static zones from the map's zone tree, then player-city zones from the DB. False aborts boot. // Read before the zone templates, so only the templates this map uses are parsed. False aborts boot.
public static boolean loadZones(int mapID) { public static boolean loadZoneTree(int mapID) {
ArrayList<ZoneTreeEntry> zoneTree;
ArrayList<Zone> cityZones;
Zone zone;
int staticCount = 0;
boolean templatesMissing = false;
zoneTree = ZoneTreeParser.parseZoneTree(mapID); zoneTree = ZoneTreeParser.parseZoneTree(mapID);
if (zoneTree == null) return zoneTree != null;
return false; }
public static TreeSet<Integer> getZoneTreeTemplateIDs() {
TreeSet<Integer> templateIDs = new TreeSet<>();
for (ZoneTreeEntry treeEntry : zoneTree) for (ZoneTreeEntry treeEntry : zoneTree)
if (!treeEntry.isPlayerCity && !_zone_templates.containsKey(treeEntry.template)) { templateIDs.add(treeEntry.template);
Logger.error("Zone " + treeEntry.uid + " (" + treeEntry.name + ") has no CZONE template " + treeEntry.template);
templatesMissing = true;
}
if (templatesMissing) return templateIDs;
return false; }
// Player-city zones in the tree are a recording's cities; the live ones come from the DB // Static zones from the map's zone tree, then player-city zones from the DB. False aborts boot.
public static boolean loadZones() {
ArrayList<Zone> cityZones;
Zone zone;
int staticCount = 0;
for (ZoneTreeEntry treeEntry : zoneTree) { for (ZoneTreeEntry treeEntry : zoneTree) {
if (treeEntry.isPlayerCity)
continue;
if (treeEntry.parent != 0 && getZoneByUUID(treeEntry.parent) == null) { if (treeEntry.parent != 0 && getZoneByUUID(treeEntry.parent) == null) {
Logger.error("Zone " + treeEntry.uid + " (" + treeEntry.name + ") has no parent zone " + treeEntry.parent); Logger.error("Zone " + treeEntry.uid + " (" + treeEntry.name + ") has no parent zone " + treeEntry.parent);
return false; return false;
+15 -7
View File
@@ -21,25 +21,33 @@ import org.json.JSONObject;
import org.pmw.tinylog.Logger; import org.pmw.tinylog.Logger;
import java.io.File; import java.io.File;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
// Loads zone templates from the client cache JSON (CZONE/<id>.json). // Loads zone templates from the client cache JSON (CZONE/<id>.json), only those the map's zone tree uses.
public class ZoneParser { public class ZoneParser {
private static final String zonePath = ConfigManager.DEFAULT_DATA_DIR + "CZONE/"; private static final String zonePath = ConfigManager.DEFAULT_DATA_DIR + "CZONE/";
public static HashMap<Integer, CZoneTemplate> parseZoneFolder() { public static HashMap<Integer, CZoneTemplate> parseZoneTemplates(Collection<Integer> templateIds) {
HashMap<Integer, CZoneTemplate> zoneTemplates = new HashMap<>(); HashMap<Integer, CZoneTemplate> zoneTemplates = new HashMap<>();
File[] zoneFiles = CacheParserUtil.listJsonFiles(zonePath); File zoneFile;
int templateId;
JSONObject zoneJson; JSONObject zoneJson;
int failedCount = 0; int failedCount = 0;
for (File zoneFile : zoneFiles) { for (int templateId : templateIds) {
zoneFile = new File(zonePath + templateId + ".json");
if (!zoneFile.exists()) {
Logger.error("Zone template " + templateId + " has no CZONE file: " + zoneFile.getAbsolutePath());
failedCount++;
continue;
}
try { try {
templateId = CacheParserUtil.templateIdFromFile(zoneFile);
zoneJson = CacheParserUtil.readJsonFile(zoneFile); zoneJson = CacheParserUtil.readJsonFile(zoneFile);
zoneTemplates.put(templateId, parseZoneEntry(templateId, zoneJson)); zoneTemplates.put(templateId, parseZoneEntry(templateId, zoneJson));
} catch (Exception e) { } catch (Exception e) {
@@ -48,7 +56,7 @@ public class ZoneParser {
} }
} }
Logger.info("Loaded " + zoneTemplates.size() + " of " + zoneFiles.length + " zone templates"); Logger.info("Loaded " + zoneTemplates.size() + " of " + templateIds.size() + " zone templates");
logCounts(zoneTemplates); logCounts(zoneTemplates);
// Partial zone data is never usable; null tells the caller to abort // Partial zone data is never usable; null tells the caller to abort
@@ -67,7 +67,6 @@ public class ZoneTreeParser {
zone.parent = zoneJson.getInt("parent"); zone.parent = zoneJson.getInt("parent");
zone.template = zoneJson.getInt("template"); zone.template = zoneJson.getInt("template");
zone.name = zoneJson.getString("name"); zone.name = zoneJson.getString("name");
zone.isPlayerCity = zoneJson.getBoolean("isPlayerCity");
zone.offset = CacheParserUtil.readVector3(zoneJson.getJSONArray("offset")); zone.offset = CacheParserUtil.readVector3(zoneJson.getJSONArray("offset"));
zone.peaceZone = zoneJson.getBoolean("peaceZone"); zone.peaceZone = zoneJson.getBoolean("peaceZone");
zone.terrain = zoneJson.getString("terrain"); zone.terrain = zoneJson.getString("terrain");
@@ -19,7 +19,6 @@ public class ZoneTreeEntry {
public int parent; public int parent;
public int template; public int template;
public String name; public String name;
public boolean isPlayerCity;
public Vector3fImmutable offset; public Vector3fImmutable offset;
public boolean peaceZone; public boolean peaceZone;
public String terrain; public String terrain;
+10 -3
View File
@@ -435,8 +435,15 @@ public class WorldServer {
Logger.info("Indexing Cache Templates"); Logger.info("Indexing Cache Templates");
CObjectIndex.build(); CObjectIndex.build();
Logger.info("Loading Zone Tree");
if (!ZoneManager.loadZoneTree(worldMapID)) {
Logger.error("ABORT! Zone tree failed to load!");
exitServer(1);
}
Logger.info("Loading Cache Zone Templates"); Logger.info("Loading Cache Zone Templates");
CZoneTemplate._zones = ZoneParser.parseZoneFolder(); CZoneTemplate._zones = ZoneParser.parseZoneTemplates(ZoneManager.getZoneTreeTemplateIDs());
if (CZoneTemplate._zones == null || CZoneTemplate._zones.isEmpty()) { if (CZoneTemplate._zones == null || CZoneTemplate._zones.isEmpty()) {
Logger.error("ABORT! Cache zone templates failed to load!"); Logger.error("ABORT! Cache zone templates failed to load!");
@@ -550,8 +557,8 @@ public class WorldServer {
Logger.info("Loading all Zones"); Logger.info("Loading all Zones");
if (!ZoneManager.loadZones(worldMapID)) { if (!ZoneManager.loadZones()) {
Logger.error("ABORT! Zone tree failed to load!"); Logger.error("ABORT! Zones failed to load!");
exitServer(1); exitServer(1);
} }