mirror of
https://github.com/ProjectSWGCore/Holocore.git
synced 2026-01-17 00:06:00 -05:00
Added array-style lookup with numbered columns in SDBs
This commit is contained in:
@@ -6,7 +6,7 @@ plugins {
|
||||
id 'idea'
|
||||
id "com.github.johnrengelman.shadow" version "4.0.4"
|
||||
id "org.javamodularity.moduleplugin" version "1.4.0"
|
||||
id "org.beryx.jlink" version "2.4.0"
|
||||
id "org.beryx.jlink" version "2.4.3"
|
||||
}
|
||||
|
||||
mainClassName = 'com.projectswg.holocore.ProjectSWG'
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -86,6 +86,10 @@ public class ProjectSWG {
|
||||
|
||||
Log.i("Holocore version: %s", VERSION);
|
||||
|
||||
if (ProjectSWG.class.getResourceAsStream("/marker.txt") == null) {
|
||||
Log.a("Failed to read Holocore resources - aborting");
|
||||
return -1;
|
||||
}
|
||||
DataManager.initialize();
|
||||
Thread.currentThread().setPriority(10);
|
||||
initializeServerFactory();
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
/***********************************************************************************
|
||||
* Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
|
||||
* *
|
||||
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
|
||||
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
|
||||
* Our goal is to create an emulator which will provide a server for players to *
|
||||
* continue playing a game similar to the one they used to play. We are basing *
|
||||
* it on the final publish of the game prior to end-game events. *
|
||||
* *
|
||||
* This file is part of Holocore. *
|
||||
* *
|
||||
* --------------------------------------------------------------------------------*
|
||||
* *
|
||||
* Holocore is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* Holocore 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***********************************************************************************/
|
||||
|
||||
package com.projectswg.holocore.resources.support.data.server_info;
|
||||
|
||||
import com.projectswg.holocore.resources.support.data.server_info.SdbLoader.SdbResultSet;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class SdbColumnArraySet {
|
||||
|
||||
private final Pattern pattern;
|
||||
private final Map<Integer, Integer> mappedColumns;
|
||||
private final AtomicReference<SdbResultSet> set;
|
||||
private final AtomicInteger arraySize;
|
||||
|
||||
private SdbColumnArraySet(@Nullable SdbResultSet set, @Language("RegExp") String regex) {
|
||||
this.pattern = Pattern.compile(regex);
|
||||
this.mappedColumns = new HashMap<>();
|
||||
this.set = new AtomicReference<>(set);
|
||||
this.arraySize = new AtomicInteger(0);
|
||||
|
||||
loadColumnInfo(set);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return arraySize.get();
|
||||
}
|
||||
|
||||
protected SdbResultSet getResultSet() {
|
||||
return set.get();
|
||||
}
|
||||
|
||||
protected Collection<Entry<Integer, Integer>> getMappedEntries() {
|
||||
return mappedColumns.entrySet();
|
||||
}
|
||||
|
||||
void loadColumnInfo(@Nullable SdbResultSet set) {
|
||||
mappedColumns.clear();
|
||||
arraySize.set(0);
|
||||
this.set.getAndSet(set);
|
||||
if (set == null)
|
||||
return;
|
||||
|
||||
int columnIndex = 0;
|
||||
for (String column : set.getColumns()) {
|
||||
Matcher matcher = pattern.matcher(column);
|
||||
boolean match = matcher.matches();
|
||||
if (match && matcher.groupCount() == 1) {
|
||||
String arrayIndexStr = matcher.group(1);
|
||||
try {
|
||||
int arrayIndex = Integer.parseUnsignedInt(arrayIndexStr);
|
||||
arraySize.updateAndGet(prevIndex -> arrayIndex >= prevIndex ? arrayIndex + 1 : prevIndex);
|
||||
mappedColumns.put(arrayIndex, columnIndex);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("invalid pattern. The first capturing group must be only digits");
|
||||
}
|
||||
} else if (match) {
|
||||
throw new IllegalArgumentException("invalid pattern. Regex must have capturing group for array index");
|
||||
}
|
||||
columnIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SdbTextColumnArraySet extends SdbColumnArraySet {
|
||||
|
||||
private String [] cachedArray;
|
||||
|
||||
SdbTextColumnArraySet(@Nullable SdbResultSet set, @Language("RegExp") String regex) {
|
||||
super(set, regex);
|
||||
this.cachedArray = null;
|
||||
}
|
||||
|
||||
public String [] getArray() {
|
||||
SdbResultSet set = getResultSet();
|
||||
String [] cachedArray = this.cachedArray;
|
||||
if (cachedArray == null || cachedArray.length != size()) {
|
||||
cachedArray = new String[size()];
|
||||
this.cachedArray = cachedArray;
|
||||
}
|
||||
|
||||
Arrays.fill(cachedArray, null);
|
||||
if (set != null) {
|
||||
for (Entry<Integer, Integer> e : getMappedEntries()) {
|
||||
cachedArray[e.getKey()] = set.getText(e.getValue());
|
||||
}
|
||||
}
|
||||
return cachedArray;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SdbIntegerColumnArraySet extends SdbColumnArraySet {
|
||||
|
||||
private int [] cachedArray;
|
||||
|
||||
SdbIntegerColumnArraySet(@Nullable SdbResultSet set, @Language("RegExp") String regex) {
|
||||
super(set, regex);
|
||||
this.cachedArray = null;
|
||||
}
|
||||
|
||||
public int [] getArray() {
|
||||
SdbResultSet set = getResultSet();
|
||||
int [] cachedArray = this.cachedArray;
|
||||
if (cachedArray == null || cachedArray.length != size()) {
|
||||
cachedArray = new int[size()];
|
||||
this.cachedArray = cachedArray;
|
||||
}
|
||||
|
||||
Arrays.fill(cachedArray, Integer.MAX_VALUE);
|
||||
if (set != null) {
|
||||
for (Entry<Integer, Integer> e : getMappedEntries()) {
|
||||
cachedArray[e.getKey()] = (int) set.getInt(e.getValue());
|
||||
}
|
||||
}
|
||||
return cachedArray;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SdbLongColumnArraySet extends SdbColumnArraySet {
|
||||
|
||||
private long [] cachedArray;
|
||||
|
||||
SdbLongColumnArraySet(@Nullable SdbResultSet set, @Language("RegExp") String regex) {
|
||||
super(set, regex);
|
||||
this.cachedArray = null;
|
||||
}
|
||||
|
||||
public long [] getArray() {
|
||||
SdbResultSet set = getResultSet();
|
||||
long [] cachedArray = this.cachedArray;
|
||||
if (cachedArray == null || cachedArray.length != size()) {
|
||||
cachedArray = new long[size()];
|
||||
this.cachedArray = cachedArray;
|
||||
}
|
||||
|
||||
Arrays.fill(cachedArray, Long.MAX_VALUE);
|
||||
if (set != null) {
|
||||
for (Entry<Integer, Integer> e : getMappedEntries()) {
|
||||
cachedArray[e.getKey()] = set.getInt(e.getValue());
|
||||
}
|
||||
}
|
||||
return cachedArray;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SdbRealColumnArraySet extends SdbColumnArraySet {
|
||||
|
||||
private double [] cachedArray;
|
||||
|
||||
SdbRealColumnArraySet(@Nullable SdbResultSet set, @Language("RegExp") String regex) {
|
||||
super(set, regex);
|
||||
this.cachedArray = null;
|
||||
}
|
||||
|
||||
public double [] getArray() {
|
||||
SdbResultSet set = getResultSet();
|
||||
double [] cachedArray = this.cachedArray;
|
||||
if (cachedArray == null || cachedArray.length != size()) {
|
||||
cachedArray = new double[size()];
|
||||
this.cachedArray = cachedArray;
|
||||
}
|
||||
|
||||
Arrays.fill(cachedArray, Double.NaN);
|
||||
if (set != null) {
|
||||
for (Entry<Integer, Integer> e : getMappedEntries()) {
|
||||
cachedArray[e.getKey()] = set.getReal(e.getValue());
|
||||
}
|
||||
}
|
||||
return cachedArray;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SdbBooleanColumnArraySet extends SdbColumnArraySet {
|
||||
|
||||
private boolean [] cachedArray;
|
||||
|
||||
SdbBooleanColumnArraySet(@Nullable SdbResultSet set, @Language("RegExp") String regex) {
|
||||
super(set, regex);
|
||||
this.cachedArray = null;
|
||||
}
|
||||
|
||||
public boolean [] getArray() {
|
||||
SdbResultSet set = getResultSet();
|
||||
boolean [] cachedArray = this.cachedArray;
|
||||
if (cachedArray == null || cachedArray.length != size()) {
|
||||
cachedArray = new boolean[size()];
|
||||
this.cachedArray = cachedArray;
|
||||
}
|
||||
|
||||
Arrays.fill(cachedArray, false);
|
||||
if (set != null) {
|
||||
for (Entry<Integer, Integer> e : getMappedEntries()) {
|
||||
cachedArray[e.getKey()] = set.getBoolean(e.getValue());
|
||||
}
|
||||
}
|
||||
return cachedArray;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,9 @@
|
||||
***********************************************************************************/
|
||||
package com.projectswg.holocore.resources.support.data.server_info;
|
||||
|
||||
import com.projectswg.holocore.resources.support.data.server_info.SdbColumnArraySet.*;
|
||||
import me.joshlarson.jlcommon.log.Log;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
@@ -65,6 +67,11 @@ public class SdbLoader {
|
||||
void close() throws IOException;
|
||||
boolean next() throws IOException;
|
||||
List<String> getColumns();
|
||||
SdbTextColumnArraySet getTextArrayParser(@Language("RegExp") String regex);
|
||||
SdbIntegerColumnArraySet getIntegerArrayParser(@Language("RegExp") String regex);
|
||||
SdbLongColumnArraySet getLongArrayParser(@Language("RegExp") String regex);
|
||||
SdbRealColumnArraySet getRealArrayParser(@Language("RegExp") String regex);
|
||||
SdbBooleanColumnArraySet getBooleanArrayParser(@Language("RegExp") String regex);
|
||||
|
||||
String getText(int index);
|
||||
String getText(String columnName);
|
||||
@@ -83,10 +90,12 @@ public class SdbLoader {
|
||||
|
||||
private final Iterator<SdbResultSet> sdbs;
|
||||
private final AtomicReference<SdbResultSet> sdb;
|
||||
private final Set<SdbColumnArraySet> arraySets;
|
||||
|
||||
private MasterSdbResultSet(Iterator<SdbResultSet> sdbs) {
|
||||
this.sdbs = sdbs;
|
||||
this.sdb = new AtomicReference<>(sdbs.hasNext() ? sdbs.next() : null);
|
||||
this.arraySets = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,6 +118,8 @@ public class SdbLoader {
|
||||
}
|
||||
set = sdbs.next();
|
||||
sdb.set(set);
|
||||
for (SdbColumnArraySet arraySet : arraySets)
|
||||
arraySet.loadColumnInfo(set);
|
||||
if (set == null)
|
||||
return false; // shouldn't be possible anyways
|
||||
}
|
||||
@@ -120,6 +131,36 @@ public class SdbLoader {
|
||||
return getResultSet().getColumns();
|
||||
}
|
||||
|
||||
private <T extends SdbColumnArraySet> T registerArrayParser(T instance) {
|
||||
arraySets.add(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbTextColumnArraySet getTextArrayParser(@Language("RegExp") String regex) {
|
||||
return registerArrayParser(new SdbTextColumnArraySet(this, regex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbIntegerColumnArraySet getIntegerArrayParser(String regex) {
|
||||
return registerArrayParser(new SdbIntegerColumnArraySet(this, regex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbLongColumnArraySet getLongArrayParser(String regex) {
|
||||
return registerArrayParser(new SdbLongColumnArraySet(this, regex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbRealColumnArraySet getRealArrayParser(String regex) {
|
||||
return registerArrayParser(new SdbRealColumnArraySet(this, regex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbBooleanColumnArraySet getBooleanArrayParser(String regex) {
|
||||
return registerArrayParser(new SdbBooleanColumnArraySet(this, regex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(int index) {
|
||||
return getResultSet().getText(index);
|
||||
@@ -227,6 +268,31 @@ public class SdbLoader {
|
||||
return Arrays.asList(columnNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbTextColumnArraySet getTextArrayParser(@Language("RegExp") String regex) {
|
||||
return new SdbTextColumnArraySet(this, regex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbIntegerColumnArraySet getIntegerArrayParser(String regex) {
|
||||
return new SdbIntegerColumnArraySet(this, regex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbLongColumnArraySet getLongArrayParser(String regex) {
|
||||
return new SdbLongColumnArraySet(this, regex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbRealColumnArraySet getRealArrayParser(String regex) {
|
||||
return new SdbRealColumnArraySet(this, regex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SdbBooleanColumnArraySet getBooleanArrayParser(String regex) {
|
||||
return new SdbBooleanColumnArraySet(this, regex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(int index) {
|
||||
return columnValues[index];
|
||||
@@ -279,33 +345,44 @@ public class SdbLoader {
|
||||
lineBuffer.clear();
|
||||
|
||||
try {
|
||||
int b;
|
||||
readLoop:
|
||||
while (true) {
|
||||
b = input.read();
|
||||
switch (b) {
|
||||
case -1:
|
||||
throw new EOFException();
|
||||
case '\n':
|
||||
if (!endsNewline)
|
||||
throw new EOFException();
|
||||
case '\t':
|
||||
break readLoop;
|
||||
case '\r':
|
||||
continue;
|
||||
default:
|
||||
lineBuffer.pushBack((char) b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fillLineBuffer(endsNewline);
|
||||
} catch (EOFException e) {
|
||||
if (lineBuffer.isEmpty())
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
if (lineBuffer.isEmpty())
|
||||
throw new EOFException();
|
||||
throw new EOFException("Expected data, got " + e.getClass().getName() + ": " + e.getMessage());
|
||||
}
|
||||
|
||||
return lineBuffer.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("HardcodedLineSeparator") // ignores \r
|
||||
private void fillLineBuffer(boolean endsNewline) throws IOException {
|
||||
BasicStringBuilder lineBuffer = this.lineBuffer;
|
||||
int b;
|
||||
readLoop:
|
||||
while (true) {
|
||||
b = input.read();
|
||||
switch (b) {
|
||||
case -1:
|
||||
throw new EOFException("Expected data, got EOF");
|
||||
case '\n':
|
||||
if (!endsNewline)
|
||||
throw new EOFException("Expected data, got newline");
|
||||
case '\t':
|
||||
break readLoop;
|
||||
case '\r':
|
||||
continue;
|
||||
default:
|
||||
lineBuffer.pushBack((char) b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("HardcodedLineSeparator") // ignores \r
|
||||
private String fetchLine() {
|
||||
lineBuffer.clear();
|
||||
try {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
package com.projectswg.holocore.resources.support.global.zone.name_filter;
|
||||
|
||||
import me.joshlarson.jlcommon.log.Log;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -36,7 +37,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class NameFilter {
|
||||
|
||||
@@ -49,10 +50,10 @@ public class NameFilter {
|
||||
private final InputStream reservedStream;
|
||||
private final InputStream fictionStream;
|
||||
|
||||
public NameFilter(InputStream profaneStream, InputStream reservedStream, InputStream fictionStream) {
|
||||
this.profaneStream = profaneStream;
|
||||
this.reservedStream = reservedStream;
|
||||
this.fictionStream = fictionStream;
|
||||
public NameFilter(@NotNull InputStream profaneStream, @NotNull InputStream reservedStream, @NotNull InputStream fictionStream) {
|
||||
this.profaneStream = Objects.requireNonNull(profaneStream, "profaneStream");
|
||||
this.reservedStream = Objects.requireNonNull(reservedStream, "reservedStream");
|
||||
this.fictionStream = Objects.requireNonNull(fictionStream, "fictionStream");
|
||||
this.profaneWords = new ArrayList<>();
|
||||
this.reservedWords = new ArrayList<>();
|
||||
this.fictionNames = new ArrayList<>();
|
||||
|
||||
@@ -11,16 +11,15 @@ import com.projectswg.holocore.intents.support.objects.items.CreateStaticItemInt
|
||||
import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent;
|
||||
import com.projectswg.holocore.resources.support.data.config.ConfigFile;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.DataManager;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.SdbColumnArraySet.SdbIntegerColumnArraySet;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.SdbColumnArraySet.SdbTextColumnArraySet;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.SdbLoader;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.SdbLoader.SdbResultSet;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.loader.NpcLoader;
|
||||
import com.projectswg.holocore.resources.support.data.server_info.loader.NpcLoader.NpcInfo;
|
||||
import com.projectswg.holocore.resources.support.global.player.Player;
|
||||
import com.projectswg.holocore.resources.support.objects.ObjectCreator;
|
||||
import com.projectswg.holocore.resources.support.objects.permissions.ContainerPermissions;
|
||||
import com.projectswg.holocore.resources.support.objects.permissions.ReadWritePermissions;
|
||||
import com.projectswg.holocore.resources.support.objects.swg.SWGObject;
|
||||
import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureDifficulty;
|
||||
import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject;
|
||||
@@ -110,6 +109,8 @@ public final class LootGenerationService extends Service {
|
||||
long startTime = StandardLog.onStartLoad("loot tables");
|
||||
|
||||
try (SdbResultSet set = SdbLoader.load(new File("serverdata/loot/loot_table.sdb"))) {
|
||||
SdbTextColumnArraySet itemGroups = set.getTextArrayParser("items_group_([0-9]+)");
|
||||
SdbIntegerColumnArraySet chanceGroups = set.getIntegerArrayParser("chance_group_([0-9]+)");
|
||||
while (set.next()) {
|
||||
String tableName = set.getText("loot_id");
|
||||
if (tableName.equals("-"))
|
||||
@@ -118,13 +119,16 @@ public final class LootGenerationService extends Service {
|
||||
LootTable table = new LootTable();
|
||||
int totalChance = 0; // Must not be above 100. Also used to convert chances in the form of "33, 33, 34" to "33, 66, 100"
|
||||
|
||||
for (int groupNum = 1; groupNum <= 16 && totalChance < 100; groupNum++) {
|
||||
int groupChance = (int) set.getInt("chance_group_" + groupNum);
|
||||
if (groupChance == 0)
|
||||
String [] itemGroupsArray = itemGroups.getArray();
|
||||
int [] chanceGroupsArray = chanceGroups.getArray();
|
||||
assert itemGroupsArray.length == chanceGroupsArray.length;
|
||||
for (int groupNum = 0; groupNum < chanceGroupsArray.length && totalChance < 100; groupNum++) {
|
||||
int groupChance = chanceGroupsArray[groupNum];
|
||||
if (groupChance == 0 || groupChance == Integer.MAX_VALUE)
|
||||
continue;
|
||||
groupChance += totalChance;
|
||||
|
||||
table.addLootGroup(new LootGroup(groupChance, set.getText("items_group_" + groupNum).split(";")));
|
||||
table.addLootGroup(new LootGroup(groupChance, itemGroupsArray[groupNum].split(";")));
|
||||
totalChance = groupChance;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,7 @@ import com.projectswg.common.data.encodables.tangible.Race;
|
||||
import com.projectswg.holocore.resources.support.global.zone.name_filter.NameFilter;
|
||||
import me.joshlarson.jlcommon.log.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -219,6 +216,8 @@ public class SWGNameGenerator {
|
||||
|
||||
RaceNameRule rule = null;
|
||||
try(InputStream stream = getClass().getResourceAsStream("/namegen/" + species + ".txt")) {
|
||||
if (stream == null)
|
||||
throw new FileNotFoundException("/namegen/"+species+".txt");
|
||||
rule = createRaceRule(stream);
|
||||
|
||||
if (rule != null)
|
||||
|
||||
0
src/main/resources/marker.txt
Normal file
0
src/main/resources/marker.txt
Normal file
Reference in New Issue
Block a user