Initial Commit
Initial commit of project.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import dataStructures.KittyChannel;
|
||||
import dataStructures.KittyGuild;
|
||||
import dataStructures.KittyRating;
|
||||
import dataStructures.KittyRole;
|
||||
import dataStructures.KittyUser;
|
||||
import dataStructures.Response;
|
||||
import dataStructures.UserInput;
|
||||
import utils.GlobalLog;
|
||||
import utils.LogFilter;
|
||||
|
||||
// One note about string[] ... We can't change whitelist
|
||||
// on the command without re-registering it.
|
||||
public abstract class Command
|
||||
{
|
||||
public ArrayList<String> registeredNames;
|
||||
private KittyRole roleLevel;
|
||||
private KittyRating contentRating;
|
||||
|
||||
public Command(KittyRole roleLevel, KittyRating contentRating)
|
||||
{
|
||||
this.registeredNames = new ArrayList<String>();
|
||||
this.roleLevel = roleLevel;
|
||||
this.contentRating = contentRating;
|
||||
}
|
||||
|
||||
private void Reject(KittyUser user, String reason)
|
||||
{
|
||||
GlobalLog.Warn(LogFilter.Command, this.getClass().getSimpleName() + " from " + user.name + " rejected due to command's " + reason);
|
||||
}
|
||||
|
||||
// Determine if we're exclusive enough for this command and
|
||||
// if the command is permitted by the guild we're in
|
||||
private boolean CanCall(KittyGuild guild, KittyChannel channel, KittyUser user)
|
||||
{
|
||||
if(guild.contentRating.getValue() < contentRating.getValue())
|
||||
{
|
||||
Reject(user, "content rating");
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO(wisp, rin): ADD CHANNEL CHECK HERE
|
||||
|
||||
if(user.GetRole().getValue() >= roleLevel.getValue())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Reject(user, "permissions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called by the Command manager - this will run the command
|
||||
// if the issuing user has the permission to do so!
|
||||
protected final void Invoke(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
|
||||
{
|
||||
if(!CanCall(guild, channel, user))
|
||||
return;
|
||||
|
||||
OnRun(guild, channel, user, input, res);
|
||||
}
|
||||
|
||||
public ArrayList<String> RegisteredNames()
|
||||
{
|
||||
return registeredNames;
|
||||
}
|
||||
|
||||
public KittyRating Rating()
|
||||
{
|
||||
return contentRating;
|
||||
}
|
||||
|
||||
public KittyRole RequiredRole()
|
||||
{
|
||||
return roleLevel;
|
||||
}
|
||||
|
||||
// OVERRIDE ME! (This is not required but advised!)
|
||||
// Returns if the command succeeded or not.
|
||||
public String HelpText()
|
||||
{
|
||||
return "No help text has been added yet for " + this.getClass().getSimpleName() + "!";
|
||||
}
|
||||
|
||||
// OVERRIDE ME! (This is required)
|
||||
// Returns if the command succeeded or not.
|
||||
public abstract void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res);
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package core;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import dataStructures.KittyChannel;
|
||||
import dataStructures.KittyGuild;
|
||||
import dataStructures.KittyUser;
|
||||
import dataStructures.Response;
|
||||
import dataStructures.UserInput;
|
||||
import utils.GlobalLog;
|
||||
import utils.LogFilter;
|
||||
|
||||
public class CommandManager
|
||||
{
|
||||
// Variables
|
||||
private HashMap<String, Command> commands;
|
||||
private ArrayList<CommandThread> threadAccumulator;
|
||||
private long invokeCount;
|
||||
|
||||
|
||||
// Default Constructor
|
||||
public CommandManager()
|
||||
{
|
||||
commands = new HashMap<String, Command>();
|
||||
threadAccumulator = new ArrayList<CommandThread>();
|
||||
invokeCount = 0;
|
||||
}
|
||||
|
||||
// Allows the command manager to keep track of a command.
|
||||
public void Register(String key, Command command)
|
||||
{
|
||||
if(key == null)
|
||||
return;
|
||||
|
||||
key = key.toLowerCase();
|
||||
command.registeredNames.add(key);
|
||||
|
||||
Command old = commands.put(key, command);
|
||||
|
||||
if(old != null)
|
||||
{
|
||||
GlobalLog.Warn(LogFilter.Core, "Writing over a command with the key " + key);
|
||||
return;
|
||||
}
|
||||
|
||||
GlobalLog.Log(LogFilter.Core, "Command registered under key " + key);
|
||||
}
|
||||
|
||||
// Registers a command under multiple names!
|
||||
public void Register(String[] keys, Command command)
|
||||
{
|
||||
for(int i = 0; i < keys.length; ++i)
|
||||
Register(keys[i], command);
|
||||
}
|
||||
|
||||
// Calls the command but on a whole new thread!
|
||||
public void InvokeOnNewThread(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response responseContext)
|
||||
{
|
||||
// This is here to prevent spinning up a thread if this wasn't even a command.
|
||||
if(input == null || !input.IsValid())
|
||||
return;
|
||||
|
||||
// Spin up a thread and begin it. The thread carries the info needed to invoke the command.
|
||||
CommandThread newThread = new CommandThread(this, input, guild, channel, user, responseContext);
|
||||
threadAccumulator.add(newThread);
|
||||
newThread.start();
|
||||
}
|
||||
|
||||
// Calls the command specified with the key, providing user information arguments, etc.
|
||||
public void Invoke(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response responseContext)
|
||||
{
|
||||
if(input == null || !input.IsValid())
|
||||
return;
|
||||
|
||||
Command command = commands.get(input.key);
|
||||
|
||||
if(command != null)
|
||||
{
|
||||
++invokeCount;
|
||||
command.Invoke(guild, channel, user, input, responseContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
GlobalLog.Warn(LogFilter.Command, "User " + user.name + " tried to invoke command that doesn't exist: " + input.key);
|
||||
}
|
||||
}
|
||||
|
||||
// Looks up command by name. If it exists, dumps help text, otherwise returns null.
|
||||
public String GetCommandHelpText(String lookup)
|
||||
{
|
||||
Command command = commands.get(lookup);
|
||||
|
||||
if(command != null)
|
||||
return command.HelpText();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Returns the number of commands sent so far during this program run
|
||||
public long GetInvokeCount()
|
||||
{
|
||||
return invokeCount;
|
||||
}
|
||||
|
||||
// Returns all commands by name
|
||||
public ArrayList<Command> GetAllRegisteredCommands()
|
||||
{
|
||||
ArrayList<Command> cmds = new ArrayList<Command>();
|
||||
for(Entry<String, Command> entry : commands.entrySet())
|
||||
cmds.add(entry.getValue());
|
||||
|
||||
return cmds;
|
||||
}
|
||||
|
||||
// Info about threads running packaged into an object
|
||||
public class ThreadData
|
||||
{
|
||||
public HashMap<Thread.State, Integer> states = new HashMap<Thread.State, Integer>();
|
||||
}
|
||||
public ThreadData DumpThreadData()
|
||||
{
|
||||
ThreadData data = new ThreadData();
|
||||
|
||||
for(int i = 0; i < threadAccumulator.size(); ++i)
|
||||
{
|
||||
Thread.State state = threadAccumulator.get(i).getState();
|
||||
Integer num = data.states.get(state);
|
||||
|
||||
if(num == null)
|
||||
num = 0;
|
||||
|
||||
data.states.put(state, num + 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package core;
|
||||
|
||||
import dataStructures.KittyChannel;
|
||||
import dataStructures.KittyGuild;
|
||||
import dataStructures.KittyUser;
|
||||
import dataStructures.Response;
|
||||
import dataStructures.UserInput;
|
||||
|
||||
public class CommandThread extends Thread
|
||||
{
|
||||
// Pile of variables
|
||||
// TODO(wisp): Consider packaging this up into a thread arguments object potentially...?
|
||||
CommandManager manager;
|
||||
UserInput input;
|
||||
KittyGuild guild;
|
||||
KittyChannel channel;
|
||||
KittyUser user;
|
||||
Response response;
|
||||
|
||||
// Constructor
|
||||
public CommandThread(CommandManager manager, UserInput input, KittyGuild guild, KittyChannel channel, KittyUser user, Response response)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.input = input;
|
||||
this.guild = guild;
|
||||
this.channel = channel;
|
||||
this.user = user;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
// Method called when spawned as a thread
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
InvokeCommand();
|
||||
}
|
||||
|
||||
private void InvokeCommand()
|
||||
{
|
||||
manager.Invoke(guild, channel, user, input, response);
|
||||
}
|
||||
|
||||
// Handles the try catch requirement java has for sleeping
|
||||
@SuppressWarnings("unused")
|
||||
private void ThreadSleep(int ms)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(ms);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package core;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import network.JDBCDriver;
|
||||
import network.JDBCDriverSQLite;
|
||||
import utils.GlobalLog;
|
||||
import utils.LogFilter;
|
||||
|
||||
// Java DataBase Connection Driver - the generic version.
|
||||
// This is where we put everything and swap out the moving parts,
|
||||
// ie MySQL, PostgreSQL, SQLite, etc...
|
||||
//
|
||||
// TODO(wisp): Right now this can be messed up with SQL injection stuff if users
|
||||
// are allowed to directly touch data. Leaving it like this temporarily.
|
||||
public class DatabaseDriver
|
||||
{
|
||||
private JDBCDriver driver;
|
||||
|
||||
// Note: Changing these values can mess up the database...
|
||||
private final String globalTableName = "kitty_globals";
|
||||
private final String globalKeyName = "GlobalKey";
|
||||
private final String globalValueName = "GlobalValue";
|
||||
|
||||
public DatabaseDriver()
|
||||
{
|
||||
driver = null;
|
||||
}
|
||||
|
||||
public boolean Connect()
|
||||
{
|
||||
driver = new JDBCDriverSQLite();
|
||||
driver.Connect();
|
||||
|
||||
// Require a global table if it doesn't exist already
|
||||
driver.ExecuteStatement("CREATE TABLE IF NOT EXISTS " + globalTableName + " (" + globalKeyName + " text PRIMARY KEY, " + globalValueName + " text);");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The key will be created if it doesn't exist and the value specified will be stored.
|
||||
public void CreateSetKey(String key, String value)
|
||||
{
|
||||
GlobalLog.Log(LogFilter.Database, "CreateSetKey: Key-" + key + " value-" + value);
|
||||
|
||||
if(HasKey(key))
|
||||
{
|
||||
UpdateKey(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateKey(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// the key will be created if it doesn't exist, and the default value returned.
|
||||
public String CreateGetKey(String key)
|
||||
{
|
||||
GlobalLog.Log(LogFilter.Database, "CreateGeyKey: key-" + key);
|
||||
|
||||
if(HasKey(key))
|
||||
{
|
||||
return GetKey(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
String newValue = "";
|
||||
CreateKey(key, newValue);
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateKey(String key, String value)
|
||||
{
|
||||
//GlobalLog.Log(LogFilter.Database, "Update key " + key);
|
||||
String command = "UPDATE " + globalTableName + " SET " + globalValueName + " = '" + value + "' WHERE " + globalKeyName + "= '" + key + "';";
|
||||
//GlobalLog.Log(LogFilter.Database, "Composed command: " + command);
|
||||
driver.ExecuteStatement(command);
|
||||
}
|
||||
|
||||
private boolean HasKey(String key)
|
||||
{
|
||||
//GlobalLog.Log(LogFilter.Database, "Has key " + key);
|
||||
String command = "SELECT COUNT(1) as count FROM " + globalTableName + " WHERE " + globalKeyName + " = '" + key + "';";
|
||||
//GlobalLog.Log(LogFilter.Database, "Composed command: " + command);
|
||||
ResultSet set = driver.ExecuteReturningStatement(command);
|
||||
String out = ResultAsString(set, "count");
|
||||
return out.charAt(0) == '1';
|
||||
}
|
||||
|
||||
private String GetKey(String key)
|
||||
{
|
||||
//GlobalLog.Log(LogFilter.Database, "Getting key " + key);
|
||||
String command = "SELECT " + globalValueName + " as searchedKey FROM " + globalTableName +" WHERE " + globalKeyName + "= \'" + key + "\';";
|
||||
//GlobalLog.Log(LogFilter.Database, "Composed command: " + command);
|
||||
ResultSet set = driver.ExecuteReturningStatement(command);
|
||||
return ResultAsString(set, "searchedKey");
|
||||
}
|
||||
|
||||
private void CreateKey(String key, String value)
|
||||
{
|
||||
//GlobalLog.Log(LogFilter.Database, "Creating Key " + key);
|
||||
String command = "INSERT INTO " + globalTableName + " (GlobalKey, GlobalValue) VALUES ('" + key + "', '" + value + "');";
|
||||
//GlobalLog.Log(LogFilter.Database, "Composed command: " + command);
|
||||
driver.ExecuteStatement(command);
|
||||
}
|
||||
|
||||
private String ResultAsString(ResultSet rs, String key)
|
||||
{
|
||||
if(rs == null)
|
||||
return "";
|
||||
|
||||
try
|
||||
{
|
||||
boolean hasKey = rs.next();
|
||||
if(hasKey)
|
||||
{
|
||||
String val = rs.getString(key);
|
||||
return val;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
GlobalLog.Error(LogFilter.Database, e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import utils.GlobalLog;
|
||||
import utils.LogFilter;
|
||||
|
||||
public class DatabaseManager
|
||||
{
|
||||
// Singleton accessor
|
||||
public static DatabaseManager instance = null;
|
||||
|
||||
// Private internal variables
|
||||
private ArrayList<DatabaseTrackedObject> trackedObjects;
|
||||
private DatabaseDriver driver;
|
||||
|
||||
public DatabaseManager()
|
||||
{
|
||||
if(instance == null)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
GlobalLog.Error(LogFilter.Database, "Attempted to register a second DataBase manager!");
|
||||
return;
|
||||
}
|
||||
|
||||
trackedObjects = new ArrayList<DatabaseTrackedObject>();
|
||||
driver = new DatabaseDriver();
|
||||
driver.Connect();
|
||||
}
|
||||
|
||||
// Thumbs through registered objects and syncs them with the database.
|
||||
// TODO(wisp) Right now this just syncs on the main thread, but we will
|
||||
// want to have upkeep commands queue up for a dedicated database thread
|
||||
// in the future to offload the wait times.
|
||||
public void Upkeep()
|
||||
{
|
||||
for(int i = 0 ; i < trackedObjects.size(); ++i)
|
||||
{
|
||||
DatabaseTrackedObject dto = trackedObjects.get(i);
|
||||
|
||||
if(dto.IsDirty())
|
||||
{
|
||||
SetRemoteValue(dto.identifier, dto.Serialize());
|
||||
dto.Resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Register(DatabaseTrackedObject tracked)
|
||||
{
|
||||
trackedObjects.add(tracked);
|
||||
tracked.DeSerialzie(GetRemoteValue(tracked.identifier));
|
||||
}
|
||||
|
||||
public String GetRemoteValue(String key)
|
||||
{
|
||||
return driver.CreateGetKey(key);
|
||||
}
|
||||
|
||||
public void SetRemoteValue(String key, String value)
|
||||
{
|
||||
driver.CreateSetKey(key, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package core;
|
||||
|
||||
public abstract class DatabaseTrackedObject
|
||||
{
|
||||
private boolean isDirty;
|
||||
public final String identifier;
|
||||
|
||||
public DatabaseTrackedObject(String identifier)
|
||||
{
|
||||
this.isDirty = false;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public final boolean IsDirty()
|
||||
{
|
||||
return isDirty;
|
||||
}
|
||||
|
||||
public final void MarkDirty()
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
public final void Resolve()
|
||||
{
|
||||
isDirty = false;
|
||||
}
|
||||
|
||||
// TODO(wisp): Not the best way to handle this, potentially consider
|
||||
// using an object factory that looks up how to serialize and
|
||||
// deserialize based on the type of the thing being tracked.
|
||||
// For now, this is fine.
|
||||
public abstract String Serialize();
|
||||
public abstract void DeSerialzie(String string);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package core;
|
||||
|
||||
public class GenericImage
|
||||
{
|
||||
private String artist;
|
||||
private String postURL;
|
||||
private String imageURL;
|
||||
|
||||
public GenericImage(String artist, String postURL, String imageURL)
|
||||
{
|
||||
this.artist = artist;
|
||||
this.postURL = postURL;
|
||||
this.imageURL = imageURL;
|
||||
}
|
||||
|
||||
public void editArtist(String artist)
|
||||
{
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public void editPostURL(String postURL)
|
||||
{
|
||||
this.postURL = postURL;
|
||||
}
|
||||
|
||||
public void editImageURL(String imageURL)
|
||||
{
|
||||
this.imageURL = imageURL;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
if(imageURL.isEmpty())
|
||||
{
|
||||
return "I couldn't find anything! Please try again!";
|
||||
}
|
||||
return "Artist: " + artist + "\n<" + postURL.trim() + ">\n" + imageURL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
package core;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import commands.*;
|
||||
import dataStructures.*;
|
||||
import net.dv8tion.jda.core.entities.Member;
|
||||
import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
|
||||
import utils.GlobalLog;
|
||||
import utils.LogFilter;
|
||||
|
||||
// NOTE(wisp): Isolated factory to assist with storage and caching if needed.
|
||||
// This also minimizes the number of places JDA interacts with our codebase.
|
||||
// As it stands, if the object name begins with Kitty, it's constructed here.
|
||||
// TODO: Make all methods ID based instead of event based
|
||||
public class ObjectBuilderFactory
|
||||
{
|
||||
// Key: guild string id, Value: guild information
|
||||
private static HashMap<String, KittyGuild> guildCache;
|
||||
|
||||
// Key: channel string id, Value: channel information
|
||||
private static HashMap<String, KittyChannel> channelCache;
|
||||
|
||||
// Key: guild string id + user string id, Value: user information
|
||||
private static HashMap<String, KittyUser> userCache;
|
||||
|
||||
// For tracking and managing object sync
|
||||
private static DatabaseManager database;
|
||||
|
||||
// Stats tracking and whatnot... for setting stats too internally potentially
|
||||
private static Stats stats;
|
||||
|
||||
//RPManger for tracking RP system
|
||||
private static RPManager rpManager;
|
||||
|
||||
// NOTE(wisp): This is designed to initialize at the last possible second.
|
||||
// The idea behind this is that there may be other things that may need
|
||||
// time to initialize before the factory can use them, and this guarantees
|
||||
// they get the time they need.
|
||||
private static boolean hasInitialized;
|
||||
private static Semaphore initMutex = new Semaphore(1);
|
||||
private static void LazyInit()
|
||||
{
|
||||
if(hasInitialized)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
initMutex.acquire();
|
||||
try
|
||||
{
|
||||
// NOTE(wisp): Actually put all the init code here.
|
||||
// In the future, this is where we would read from something external.
|
||||
guildCache = new HashMap<String, KittyGuild>();
|
||||
userCache = new HashMap<String, KittyUser>();
|
||||
channelCache = new HashMap<String, KittyChannel>();
|
||||
database = null;
|
||||
stats = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
initMutex.release();
|
||||
}
|
||||
}
|
||||
catch(InterruptedException ie)
|
||||
{
|
||||
GlobalLog.Error(LogFilter.Core, "Issue during object builder lazy initialization."
|
||||
+ " The factory was not initialized, "
|
||||
+ "and kitty will not be able to continue functionally.");
|
||||
}
|
||||
|
||||
hasInitialized = true;
|
||||
}
|
||||
|
||||
// Explicitly locks: guildCache
|
||||
public static KittyGuild ExtractGuild(GuildMessageReceivedEvent event)
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
// Look up the guild. This process can only happen in a single-threaded way
|
||||
// because of the nature of the cache. We wait until the last second to
|
||||
// look up the guild.
|
||||
String uid = event.getGuild().getId();
|
||||
|
||||
// ince we're lazily initialized, we can synchronize w/ the
|
||||
// guildCache object now instead of having to use a mutex.
|
||||
KittyGuild guild = null;
|
||||
synchronized (guildCache)
|
||||
{
|
||||
KittyGuild cachedGuild = guildCache.get(uid);
|
||||
if(cachedGuild != null)
|
||||
{
|
||||
guild = cachedGuild;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct a new guild with defaults
|
||||
guild = new KittyGuild(uid);
|
||||
DatabaseManager.instance.Register(guild);
|
||||
guildCache.put(uid, guild);
|
||||
}
|
||||
}
|
||||
|
||||
return guild;
|
||||
}
|
||||
|
||||
// Explicitly locks: guildCache
|
||||
public static KittyRole ExtractRole(GuildMessageReceivedEvent event)
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
// Looks up the user role. If none is found we check to see if they own
|
||||
// the guild if not, they're assumed to be
|
||||
// allowed to use the bot at a general level.
|
||||
KittyRole role = KittyRole.General;
|
||||
|
||||
if(event.getAuthor().getId() == event.getGuild().getOwner().getUser().getId())
|
||||
{
|
||||
role = KittyRole.Admin;
|
||||
}
|
||||
|
||||
String uid = event.getGuild().getId() + event.getAuthor().getId();
|
||||
synchronized(userCache)
|
||||
{
|
||||
KittyUser cachedUser = userCache.get(uid);
|
||||
if(cachedUser != null)
|
||||
role = cachedUser.GetRole();
|
||||
}
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
// Explicitly locks: guildCache
|
||||
// Extracts the content rating information it can from the provided event.
|
||||
public static KittyRating ExtractContentRating(GuildMessageReceivedEvent event)
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
// Look up content rating of the guild, returns a safe content rating.
|
||||
KittyRating contentRating = KittyRating.Safe;
|
||||
String uid = event.getGuild().getId();
|
||||
synchronized(guildCache)
|
||||
{
|
||||
KittyGuild cachedGuild = guildCache.get(uid);
|
||||
if(cachedGuild != null)
|
||||
contentRating = cachedGuild.contentRating;
|
||||
}
|
||||
|
||||
return contentRating;
|
||||
}
|
||||
|
||||
// Implicitly locks guild cache by calling ExtractGuild
|
||||
public static KittyChannel ExtractChannel(GuildMessageReceivedEvent event)
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
String channelID = event.getChannel().getId();
|
||||
String guildID = event.getGuild().getId();
|
||||
KittyChannel channel = null;
|
||||
|
||||
synchronized(channelCache)
|
||||
{
|
||||
KittyChannel cachedChannel = channelCache.get(channelID);
|
||||
|
||||
if(cachedChannel != null)
|
||||
{
|
||||
channel = cachedChannel;
|
||||
}
|
||||
else
|
||||
{
|
||||
KittyGuild cachedGuild = guildCache.get(guildID);
|
||||
channel = new KittyChannel(channelID, cachedGuild);
|
||||
channelCache.put(channelID, channel);
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
// Implicitly locks guild cache by calling ExtractRole and ExtractGuild
|
||||
public static KittyUser ExtractUser(GuildMessageReceivedEvent event)
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
String uid = event.getGuild().getId() + event.getAuthor().getId();
|
||||
KittyUser user = null;
|
||||
synchronized(userCache)
|
||||
{
|
||||
KittyUser cachedUser = userCache.get(uid);
|
||||
if(cachedUser != null)
|
||||
{
|
||||
updateUser(cachedUser, event.getMember());
|
||||
user = cachedUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
KittyRole role = ExtractRole(event);
|
||||
KittyGuild guild = ExtractGuild(event);
|
||||
|
||||
String name;
|
||||
if(event.getMember().getNickname() == null)
|
||||
name = event.getAuthor().getName();
|
||||
else
|
||||
name = event.getMember().getNickname();
|
||||
|
||||
String discordID = event.getMember().getUser().getId();
|
||||
String avatarID = event.getAuthor().getAvatarUrl();
|
||||
user = new KittyUser(name, guild, role, uid, avatarID, discordID);
|
||||
DatabaseManager.instance.Register(user);
|
||||
userCache.put(uid, user);
|
||||
}
|
||||
}
|
||||
|
||||
if(event.getMessage().getMentionedMembers().isEmpty())
|
||||
return user;
|
||||
|
||||
Member mentioned;
|
||||
for(int i = 0; i < event.getMessage().getMentionedMembers().size(); i++)
|
||||
{
|
||||
mentioned = event.getMessage().getMentionedMembers().get(i);
|
||||
if(mentioned.getNickname() != null)
|
||||
ExtractUserByJDAUser(event.getGuild().getId(), mentioned.getNickname(),
|
||||
mentioned.getUser().getId(), mentioned.getUser().getAvatarUrl(), mentioned.getUser().getId());
|
||||
else
|
||||
ExtractUserByJDAUser(event.getGuild().getId(), mentioned.getUser().getName(),
|
||||
mentioned.getUser().getId(), mentioned.getUser().getAvatarUrl(), mentioned.getUser().getId());
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public static KittyUser ExtractUserByJDAUser(String guildID, String name, String userID, String avatarID, String discordID)
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
String uid = guildID + userID;
|
||||
KittyUser user = null;
|
||||
synchronized(userCache)
|
||||
{
|
||||
KittyUser cachedUser = userCache.get(uid);
|
||||
if(cachedUser != null)
|
||||
{
|
||||
if(name != null)
|
||||
cachedUser.name = name;
|
||||
cachedUser.avatarID = avatarID;
|
||||
user = cachedUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
KittyRole role = KittyRole.General;
|
||||
KittyGuild guild = guildCache.get(guildID);
|
||||
user = new KittyUser(name, guild, role, uid, avatarID, discordID);
|
||||
DatabaseManager.instance.Register(user);
|
||||
userCache.put(uid, user);
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public static KittyUser getCachedUser(String guildID, String userID)
|
||||
{
|
||||
String uid = guildID + userID;
|
||||
KittyUser user = null;
|
||||
synchronized(userCache)
|
||||
{
|
||||
user = userCache.get(uid);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public static void updateUser(KittyUser user, Member member)
|
||||
{
|
||||
if(member.getNickname() == null)
|
||||
user.name = member.getUser().getName();
|
||||
else
|
||||
user.name = member.getNickname();
|
||||
|
||||
user.avatarID = member.getUser().getAvatarUrl();
|
||||
}
|
||||
|
||||
// Default construction of the command manager.
|
||||
// TODO(wisp): We want to be able to keep all this data
|
||||
// stored off in a file at some point, so we can reflect it onto the
|
||||
// project and build it per-guild. That's for later now tho.
|
||||
public static CommandManager ConstructCommandManager()
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
CommandManager manager = new CommandManager();
|
||||
|
||||
manager.Register("test", new CommandTesting(KittyRole.Dev, KittyRating.Safe));
|
||||
manager.Register("work", new CommandDoWork(KittyRole.Dev, KittyRating.Safe));
|
||||
manager.Register("shutdown", new CommandShutdown(KittyRole.Dev, KittyRating.Safe));
|
||||
manager.Register("stats", new CommandStats(KittyRole.Dev, KittyRating.Safe));
|
||||
manager.Register("invite", new CommandInvite(KittyRole.Dev, KittyRating.Safe));
|
||||
manager.Register("buildHelp", new CommandHelpBuilder(KittyRole.Dev, KittyRating.Safe));
|
||||
|
||||
manager.Register("rating", new CommandRating(KittyRole.Admin, KittyRating.Safe));
|
||||
manager.Register("indicator", new CommandChangeIndicator(KittyRole.Admin, KittyRating.Safe));
|
||||
|
||||
manager.Register("poll", new CommandPollManage(KittyRole.Mod, KittyRating.Safe));
|
||||
manager.Register("givebeans", new CommandGiveBeans(KittyRole.Mod, KittyRating.Safe));
|
||||
manager.Register("rpg", new CommandRPG(KittyRole.Mod, KittyRating.Safe));
|
||||
|
||||
manager.Register(new String[]{"perish", "thenperish"}, new CommandPerish(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("yeet", new CommandYeet(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("ping", new CommandPing(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("boop", new CommandBoop(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("roll", new CommandRoll(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("choose", new CommandChoose(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("help", new CommandHelp(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register(new String[] {"info", "about"}, new CommandInfo(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("vote", new CommandPollVote(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("results", new CommandPollResults(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("showpoll", new CommandPollShow(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("wolfram", new CommandWolfram(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register(new String[] {"c++", "g++", "cplus",}, new CommandColiru(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register(new String[] {"java", "jdoodle" }, new CommandJDoodle(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("beans", new CommandBeansShow(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("role", new CommandRole(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("bet", new CommandBetBeans(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("map", new CommandMap(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("rpstart", new CommandRPStart(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("rpend", new CommandRPEnd(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register(new String[] {"tony", "stark", "dontfeelgood", "dontfeelsogood"}, new CommandStark(KittyRole.General, KittyRating.Safe));
|
||||
manager.Register("blur", new CommandBlurry(KittyRole.General, KittyRating.Safe));
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
// NOTE(wisp): Default database manager construction. It can be constructed
|
||||
// in different ways, and so we construct it outside of the constructor for
|
||||
// the factory since it doesn't have to be present / can be elsewhere.
|
||||
// Effectively we cache the database here.
|
||||
public static DatabaseManager ConstructDatabaseManager()
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
if(database == null)
|
||||
database = new DatabaseManager();
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
public static Stats ConstructStats(CommandManager manager)
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
if(stats == null)
|
||||
stats = new Stats(manager);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
public static RPManager ConstructRPManager()
|
||||
{
|
||||
LazyInit();
|
||||
|
||||
if(rpManager == null)
|
||||
rpManager = new RPManager();
|
||||
|
||||
return rpManager;
|
||||
}
|
||||
|
||||
public static Integer GetGuildCount()
|
||||
{ synchronized(guildCache)
|
||||
{
|
||||
return guildCache.size();
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer GetUserCount()
|
||||
{ synchronized(userCache)
|
||||
{
|
||||
return userCache.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package core;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import dataStructures.*;
|
||||
import net.dv8tion.jda.core.JDA;
|
||||
import utils.GlobalLog;
|
||||
import utils.LogFilter;
|
||||
|
||||
public class RPManager
|
||||
{
|
||||
static HashMap <Long, KittyRP> logs = new HashMap<Long,KittyRP> ();
|
||||
public static RPManager instance = null;
|
||||
|
||||
public RPManager()
|
||||
{
|
||||
if(instance == null)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
GlobalLog.Error(LogFilter.Core, "Attempted to create a second RP Manager!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public String newRP(KittyChannel channel, ArrayList <KittyUser> users)
|
||||
{
|
||||
if(logs.containsKey(Long.parseLong(channel.uniqueID)))
|
||||
return "You can't have 2 RP's running at the same time!";
|
||||
else
|
||||
logs.put(Long.parseLong(channel.uniqueID), new KittyRP(users, channel));
|
||||
return "RP started!";
|
||||
}
|
||||
|
||||
public void addLine(KittyChannel channel, KittyUser user, UserInput input)
|
||||
{
|
||||
if(logs.containsKey(Long.parseLong(channel.uniqueID)) && !input.IsValid())
|
||||
logs.get(Long.parseLong(channel.uniqueID)).addLine(user, input.message);
|
||||
}
|
||||
|
||||
public File endRP(KittyChannel channel, KittyUser user) throws FileNotFoundException, UnsupportedEncodingException
|
||||
{
|
||||
if(logs.containsKey(Long.parseLong(channel.uniqueID)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
File log = logs.get(Long.parseLong(channel.uniqueID)).endRP(user);
|
||||
if(log != null)
|
||||
logs.remove(Long.parseLong(channel.uniqueID));
|
||||
return log;
|
||||
}
|
||||
|
||||
public static void Upkeep(JDA kitty)
|
||||
{
|
||||
Response res = new Response(null, kitty);
|
||||
String reminder = "";
|
||||
ArrayList<Long> users;
|
||||
long currentTime = System.currentTimeMillis();
|
||||
for (Entry<Long, KittyRP> entry : logs.entrySet())
|
||||
{
|
||||
if(currentTime > entry.getValue().getTimer() + 1000 * 60 * 30)
|
||||
{
|
||||
reminder = "Don't forget about your RP log!";
|
||||
users = entry.getValue().getUsers();
|
||||
for(int i = 0; i < users.size(); i ++)
|
||||
{
|
||||
reminder += " <@" + users.get(i) + ">";
|
||||
}
|
||||
res.CallToChannel(reminder, entry.getValue().getChannel().uniqueID);
|
||||
reminder = "";
|
||||
|
||||
entry.getValue().resetTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package core;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import core.CommandManager.ThreadData;
|
||||
import utils.GlobalLog;
|
||||
import utils.LogFilter;
|
||||
|
||||
// NOTE(wisp): This is a class designed to be asked about various kittybot stats
|
||||
public class Stats
|
||||
{
|
||||
public static String botName = "KittyBot";
|
||||
public static Stats instance = null;
|
||||
|
||||
// Internal
|
||||
private boolean isShuttingDown;
|
||||
private long messagesSeen;
|
||||
private long initTimeMS;
|
||||
|
||||
private CommandManager commandManager;
|
||||
private OperatingSystemMXBean osBean;
|
||||
|
||||
public Stats(CommandManager manager)
|
||||
{
|
||||
if(instance == null)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
GlobalLog.Error(LogFilter.Core, "Attempted to create a second Stats singleton!");
|
||||
return;
|
||||
}
|
||||
|
||||
isShuttingDown = false;
|
||||
messagesSeen = 0;
|
||||
initTimeMS = System.currentTimeMillis();
|
||||
|
||||
commandManager = manager;
|
||||
osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
|
||||
}
|
||||
|
||||
public void NoteMessageEvent()
|
||||
{
|
||||
++messagesSeen;
|
||||
}
|
||||
|
||||
public void IndicateShutdown()
|
||||
{
|
||||
synchronized(instance)
|
||||
{
|
||||
isShuttingDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean GetIsShuttingDown()
|
||||
{
|
||||
synchronized(instance)
|
||||
{
|
||||
return isShuttingDown;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// All the functions to look up stats! //
|
||||
/////////////////////////////////////////
|
||||
|
||||
// Formatted as HH:MM:SS
|
||||
public String GetFormattedUptime()
|
||||
{
|
||||
long dif = System.currentTimeMillis() - initTimeMS;
|
||||
|
||||
return String.format("%02d:%02d:%02d",
|
||||
TimeUnit.MILLISECONDS.toHours(dif),
|
||||
TimeUnit.MILLISECONDS.toMinutes(dif) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(dif)),
|
||||
TimeUnit.MILLISECONDS.toSeconds(dif) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(dif)));
|
||||
}
|
||||
|
||||
// Get number of commands that kitty has run!
|
||||
public long GetCommandsProcessed()
|
||||
{
|
||||
return commandManager.GetInvokeCount();
|
||||
}
|
||||
|
||||
public long GetMessagesSeen()
|
||||
{
|
||||
return messagesSeen;
|
||||
}
|
||||
|
||||
public double GetSystemCPULoad()
|
||||
{
|
||||
return osBean.getSystemLoadAverage();
|
||||
}
|
||||
|
||||
public long GetCPUAvailable()
|
||||
{
|
||||
return osBean.getAvailableProcessors();
|
||||
}
|
||||
|
||||
public ThreadData GetThreadData()
|
||||
{
|
||||
return commandManager.DumpThreadData();
|
||||
}
|
||||
|
||||
public int GetGuildCount()
|
||||
{
|
||||
return ObjectBuilderFactory.GetGuildCount();
|
||||
}
|
||||
|
||||
public int GetUserCount()
|
||||
{
|
||||
return ObjectBuilderFactory.GetUserCount();
|
||||
}
|
||||
|
||||
public ArrayList<Command> GetAllCommands()
|
||||
{
|
||||
return commandManager.GetAllRegisteredCommands();
|
||||
}
|
||||
|
||||
public String GetHelpText(String commandName)
|
||||
{
|
||||
return commandManager.GetCommandHelpText(commandName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package core.rpg;
|
||||
|
||||
public class RPGArmor extends RPGItem
|
||||
{
|
||||
int defense;
|
||||
|
||||
public RPGArmor()
|
||||
{
|
||||
super("Tattered Clothes", "The remains of your first sewing project. You did a pretty good job!", 5);
|
||||
defense = 1;
|
||||
}
|
||||
|
||||
public long GetDefense() { return defense; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package core.rpg;
|
||||
|
||||
public class RPGBattleContext
|
||||
{
|
||||
public RPGState stateRef;
|
||||
|
||||
public RPGBattleContext(RPGState state)
|
||||
{
|
||||
this.stateRef = state;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package core.rpg;
|
||||
|
||||
public abstract class RPGCommand
|
||||
{
|
||||
public RPGCommand() { }
|
||||
|
||||
// OVERRIDE ME
|
||||
public abstract String OnRun(RPGState state, RPGInput input);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package core.rpg;
|
||||
|
||||
public class RPGEnemy extends RPGUnit
|
||||
{
|
||||
RPGWeapon attack;
|
||||
RPGArmor armor;
|
||||
|
||||
long expValue;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package core.rpg;
|
||||
|
||||
public final class RPGExpTable
|
||||
{
|
||||
public static long EXPFloor(long level)
|
||||
{
|
||||
if(level > Levels.length - 1)
|
||||
level = Levels.length - 1;
|
||||
|
||||
else if (level <= 0)
|
||||
level = 1;
|
||||
|
||||
return Levels[(int) level];
|
||||
}
|
||||
|
||||
public static long EXPCeil(long level)
|
||||
{
|
||||
if(level > Levels.length - 1)
|
||||
level = Levels.length - 1;
|
||||
|
||||
else if (level <= 0)
|
||||
level = 1;
|
||||
|
||||
return Levels[(int) (level + 1)];
|
||||
}
|
||||
public static long LevelFromEXP(long exp)
|
||||
{
|
||||
if(exp < 0)
|
||||
exp = 0;
|
||||
|
||||
for(int i = 0; i < Levels.length; ++i)
|
||||
{
|
||||
if(Levels[i] > exp)
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
return Levels.length - 1;
|
||||
}
|
||||
|
||||
// Based on D&D Pathfinder levels
|
||||
public static long Levels[] =
|
||||
{
|
||||
0, // level 0
|
||||
0, // level 1
|
||||
3000, // level 2
|
||||
7500, // level 3
|
||||
14000, // level 4
|
||||
23000, // level 5
|
||||
35000, // level 6
|
||||
53000, // level 7
|
||||
77000, // level 8
|
||||
115000, // level 9
|
||||
160000, // level 10
|
||||
235000, // level 11
|
||||
330000, // level 12
|
||||
475000, // level 13
|
||||
665000, // level 14
|
||||
955000, // level 15
|
||||
1350000, // level 16
|
||||
1900000, // level 17
|
||||
2700000, // level 18
|
||||
3850000, // level 19
|
||||
5350000 // level 20
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package core.rpg;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import commands.rpg.RPGCommandBattleFight;
|
||||
import commands.rpg.RPGCommandBattleRun;
|
||||
import commands.rpg.RPGCommandExplore;
|
||||
import commands.rpg.RPGCommandInfo;
|
||||
import commands.rpg.RPGCommandStats;
|
||||
import core.rpg.RPGInput;
|
||||
|
||||
// Holds the framework for the text RPG, for any number of users
|
||||
public class RPGFramework
|
||||
{
|
||||
// User ID to user state. Users are conceptually just an ID.
|
||||
public HashMap<String, RPGState> gameStates;
|
||||
public HashMap<String, RPGCommand> gameCommands;
|
||||
|
||||
// Ctor
|
||||
public RPGFramework()
|
||||
{
|
||||
this.gameStates = new HashMap<String, RPGState>();
|
||||
this.gameCommands = new HashMap<String, RPGCommand>();
|
||||
|
||||
RegisterCommand("stats", new RPGCommandStats());
|
||||
RegisterCommand("about", new RPGCommandInfo());
|
||||
RegisterCommand("info", new RPGCommandInfo());
|
||||
RegisterCommand("explore", new RPGCommandExplore());
|
||||
RegisterCommand("run", new RPGCommandBattleRun());
|
||||
RegisterCommand("fight", new RPGCommandBattleFight());
|
||||
}
|
||||
|
||||
// Primary external
|
||||
public String Run(String userID, String inputRaw)
|
||||
{
|
||||
RPGState state = LookupState(userID);
|
||||
RPGInput input = new RPGInput(inputRaw);
|
||||
return ExecuteCommand(input.key, state, input);
|
||||
}
|
||||
|
||||
// Get state for executing a command
|
||||
public RPGState LookupState(String userID)
|
||||
{
|
||||
RPGState state;
|
||||
synchronized(gameStates)
|
||||
{
|
||||
// Note: Hardcoded right now. Later: Extract.
|
||||
state = gameStates.get(userID);
|
||||
|
||||
if(state == null)
|
||||
{
|
||||
state = new RPGState(userID);
|
||||
gameStates.put(userID, state);
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// Registers a command
|
||||
private void RegisterCommand(String commandName, RPGCommand command)
|
||||
{
|
||||
commandName = commandName.toLowerCase();
|
||||
if(gameCommands.put(commandName, command) != null)
|
||||
RPGLog.Log("Managed to register the same RPG command twice! Not ideal!");
|
||||
|
||||
RPGLog.Log("Registered " + commandName);
|
||||
}
|
||||
|
||||
private String ExecuteCommand(String name, RPGState state, RPGInput input)
|
||||
{
|
||||
synchronized(gameCommands)
|
||||
{
|
||||
RPGCommand command = gameCommands.get(name.toLowerCase());
|
||||
|
||||
if(command != null && state != null)
|
||||
return command.OnRun(state, input);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package core.rpg;
|
||||
|
||||
public class RPGInput
|
||||
{
|
||||
public String raw;
|
||||
public String key;
|
||||
public String value;
|
||||
|
||||
public RPGInput(String raw)
|
||||
{
|
||||
this.raw = raw;
|
||||
this.key = "";
|
||||
this.value = "";
|
||||
|
||||
if(raw == null || raw.length() == 0)
|
||||
return;
|
||||
|
||||
raw = raw.trim();
|
||||
int whitespacePos = FindFirstWhitespace(raw);
|
||||
|
||||
if(whitespacePos == -1)
|
||||
{
|
||||
key = raw;
|
||||
return;
|
||||
}
|
||||
|
||||
key = raw.substring(0, whitespacePos).trim();
|
||||
value = raw.substring(whitespacePos).trim();
|
||||
}
|
||||
|
||||
// Finds first whitespace in the string
|
||||
private int FindFirstWhitespace(String str)
|
||||
{
|
||||
for (int i = 0; i < str.length(); ++i)
|
||||
{
|
||||
if (Character.isWhitespace(str.charAt(i)))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package core.rpg;
|
||||
|
||||
public abstract class RPGItem
|
||||
{
|
||||
protected String name;
|
||||
protected String description;
|
||||
protected int value;
|
||||
|
||||
// Defaults
|
||||
public RPGItem() { this("unknown"); }
|
||||
public RPGItem(String name) { this(name, "nothig is known about this item"); }
|
||||
public RPGItem(String name, String description) { this(name, description, 1); }
|
||||
|
||||
// Ctor
|
||||
public RPGItem(String name, String description, int value)
|
||||
{
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String GetName() { return name; }
|
||||
public String GetDescription() { return description; }
|
||||
public long GetValue() { return value; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package core.rpg;
|
||||
|
||||
import utils.GlobalLog;
|
||||
import utils.LogFilter;
|
||||
|
||||
public class RPGLog
|
||||
{
|
||||
public static void Log(String toWrite)
|
||||
{
|
||||
GlobalLog.Log(LogFilter.Command, "[RPG] " + toWrite);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package core.rpg;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
public class RPGMain
|
||||
{
|
||||
private static RPGFramework framework;
|
||||
private static Scanner scanner;
|
||||
|
||||
public static void RPGmain(String[] args)
|
||||
{
|
||||
Init();
|
||||
while(Run()) { };
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
// Initializes stuff (small factory, ish)
|
||||
private static void Init()
|
||||
{
|
||||
framework = new RPGFramework();
|
||||
scanner = new Scanner(System.in);
|
||||
}
|
||||
|
||||
// Main loop
|
||||
private static boolean Run()
|
||||
{
|
||||
// move outside
|
||||
String out = framework.Run("Test", scanner.nextLine());
|
||||
RPGLog.Log(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
private static void Shutdown()
|
||||
{
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package core.rpg;
|
||||
|
||||
public class RPGPlayer extends RPGUnit
|
||||
{
|
||||
private String name;
|
||||
private long gold;
|
||||
private long exp;
|
||||
|
||||
RPGWeapon weapon;
|
||||
RPGArmor armor;
|
||||
|
||||
public RPGPlayer()
|
||||
{
|
||||
super();
|
||||
|
||||
name = "Wanderer";
|
||||
healthCurrent = 5;
|
||||
healthMax = 5;
|
||||
gold = 0;
|
||||
weapon = new RPGWeapon();
|
||||
armor = new RPGArmor();
|
||||
}
|
||||
|
||||
// Getters
|
||||
public long GetEXP() { return exp; }
|
||||
public long GetGold() { return gold; }
|
||||
public String GetName() { return name; }
|
||||
public RPGArmor GetArmor() { return armor; };
|
||||
public RPGWeapon GetWeapon() { return weapon; };
|
||||
|
||||
// Setters
|
||||
public void SetName(String name) { this.name = name; }
|
||||
|
||||
// Interactions
|
||||
public void ApplyEXP(int expToGive)
|
||||
{
|
||||
if(expToGive < 0)
|
||||
expToGive = 0;
|
||||
|
||||
exp += expToGive;
|
||||
}
|
||||
|
||||
public void GiveGold(long amount)
|
||||
{
|
||||
if(amount < 0)
|
||||
amount = 0;
|
||||
|
||||
gold += amount;
|
||||
}
|
||||
|
||||
public void SpendGold(long amount)
|
||||
{
|
||||
if(amount < 0)
|
||||
amount = 0;
|
||||
|
||||
gold -= amount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package core.rpg;
|
||||
|
||||
// Holds specific state information for a given user and their world
|
||||
public class RPGState
|
||||
{
|
||||
// General
|
||||
public String userID;
|
||||
|
||||
// Stats and gameplay
|
||||
public RPGPlayer player;
|
||||
public RPGBattleContext battleContext;
|
||||
|
||||
public RPGState(String userID)
|
||||
{
|
||||
this.userID = userID;
|
||||
this.player = new RPGPlayer();
|
||||
this.battleContext = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package core.rpg;
|
||||
|
||||
public abstract class RPGUnit
|
||||
{
|
||||
protected int healthMax;
|
||||
protected int healthCurrent;
|
||||
|
||||
public int GetHealthMax() { return healthMax; }
|
||||
public int GetHealthCurrent() { return healthCurrent; }
|
||||
public boolean IsAlive() { return healthCurrent > 0; }
|
||||
|
||||
// Generic implementation. Consider implementing armor in
|
||||
// derived classes, for example.
|
||||
public void ApplyDamage(int value)
|
||||
{
|
||||
if(value < 0)
|
||||
value = 0;
|
||||
|
||||
healthCurrent -= value;
|
||||
}
|
||||
|
||||
// Generic implementation. Consider applying boosts in
|
||||
// derived classes, for example.
|
||||
public void ApplyHealing(int value)
|
||||
{
|
||||
if(value < 0)
|
||||
value = 0;
|
||||
|
||||
healthCurrent += value;
|
||||
|
||||
if(healthCurrent > healthMax)
|
||||
healthCurrent = healthMax;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package core.rpg;
|
||||
|
||||
public class RPGWeapon extends RPGItem
|
||||
{
|
||||
private long attack;
|
||||
private double accuracy;
|
||||
|
||||
RPGWeapon()
|
||||
{
|
||||
super("Singed Stick", "A really cool stick that you found! You poked at your campfire last night with it a bit, so the end is a bit toasty.", 2);
|
||||
attack = 1;
|
||||
accuracy = 0.8;
|
||||
}
|
||||
|
||||
public long GetAttack() { return attack; }
|
||||
public double GetAccuracy() { return accuracy; }
|
||||
}
|
||||
Reference in New Issue
Block a user