mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-03 00:38:21 +00:00
default apollo
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
Copyright (c) 2010-2011 Graham Edgecombe
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
Apollo is a RuneScape emulator which aims to encourage a fundamentally-different alternative to the way in which private server development is done today. It consists of a high-performance, modular server written in Java as well as a collection of utilities for managing the data files and plugins.
|
||||
|
||||
Currently people download a server, read through tutorials and apply their modifications or write their own code on top of it. They'll then host it or release it. The result is we currently have a complete mess of servers (this includes every current base - Hyperion, rs2hd, winterlove) all created from cobbled-together code.
|
||||
|
||||
Apollo is going to change that through its plugin system. Instead of throwing everything into one big application, Apollo consists of:
|
||||
|
||||
A small 'core' application which provides features necessary for the server to operate.
|
||||
A set of plugins, and the tools to manage them (install, uninstall, publish, download, etc).
|
||||
Some additional tools and utilities for managing the data files.
|
||||
|
||||
|
||||
This will make it much easier for everyone to develop a private server, no longer restricting it to people who can (or can't as the case is here) program. Instead of messing around copying and pasting lines of code from a woodcutting tutorial, people will simply have to download and install a woodcutting plugin.
|
||||
|
||||
There is also a plan to have a plugin repository (or maybe multiple repositories) and a set of tools to make the experience very much like a package manager on Linux.
|
||||
|
||||
It also means updates can be provided for the core server (e.g. security, stability, optimizations) very easily. Users will just have to download the new jar, overwrite the current one with the new one, and reboot their server.
|
||||
|
||||
And best of all, inexperienced users are protected from making fatal mistakes in the core and are kept away from stuff that they shouldn't be editing at their experience level.
|
||||
|
||||
Plugins are currently written in Ruby, however, in the future other languages could be added.
|
||||
|
||||
Core Features
|
||||
|
||||
Some of the significant (technically) current core features include:
|
||||
|
||||
Packet encoding/decoding has been split from the representations of the packets themselves. This allows the potential for encoding/decoding to go on in parallel and also allows multiple revisions to be supported. Currently 317 and 377 are both completely supported.
|
||||
Update server support (JAGGRAB, ondemand and HTTP).
|
||||
Packet handler chaining: this allows multiple plugins to be able to intercept a single packet and deal with it appropriately. For example, a quest plugin could intercept searching a bookshelf for instance, if the behaviour needed to change in certain cases.
|
||||
Parallel execution of player updating for multi-core machines - this has a significant benefit on my dual core machine used to test the server.
|
||||
|
||||
|
||||
As well as that, it has the bog standard stuff:
|
||||
|
||||
Login
|
||||
Appearance updating
|
||||
Multiplayer
|
||||
Walking/running
|
||||
Rights management
|
||||
Travel back algorithm for movement
|
||||
Character design
|
||||
Chatting
|
||||
Commands
|
||||
Inventory support
|
||||
Equipment support
|
||||
Animations
|
||||
Graphics
|
||||
Facing/turn to
|
||||
Action system
|
||||
Working distanced actions
|
||||
All data types implemented
|
||||
Task scheduler based on game ticks
|
||||
Saving/loading with a custom binary format
|
||||
Skill levels/experiences
|
||||
Plugin management
|
||||
Reads item information from the cache
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="apollo" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
|
||||
<property name="root" location="." />
|
||||
<property name="src" location="src" />
|
||||
<property name="test" location="test" />
|
||||
<property name="bin" location="bin" />
|
||||
<property name="lib" location="lib" />
|
||||
<property name="doc" location="doc" />
|
||||
<property name="version" value="377" />
|
||||
|
||||
<path id="binaries">
|
||||
<pathelement path="${bin}" />
|
||||
</path>
|
||||
|
||||
<path id="libraries">
|
||||
<fileset dir="${lib}">
|
||||
<include name="*.jar" />
|
||||
</fileset>
|
||||
</path>
|
||||
|
||||
<path id="master">
|
||||
<path refid="binaries" />
|
||||
<path refid="libraries" />
|
||||
</path>
|
||||
|
||||
<fileset id="sources" dir="${src}">
|
||||
<include name="**/*.java" />
|
||||
</fileset>
|
||||
|
||||
<fileset id="tests" dir="${bin}">
|
||||
<include name="**/*Test*.class" />
|
||||
<exclude name="**/*$*.class" />
|
||||
</fileset>
|
||||
|
||||
<target name="init">
|
||||
<mkdir dir="${bin}" />
|
||||
<mkdir dir="${lib}" />
|
||||
<mkdir dir="${doc}" />
|
||||
</target>
|
||||
|
||||
<target name="resolve" depends="init">
|
||||
<ivy:retrieve pattern="${lib}/[artifact]-[revision].[ext]" sync="true" />
|
||||
</target>
|
||||
|
||||
<target name="build" depends="init">
|
||||
<javac srcdir="${src}:${test}" destdir="${bin}" includeantruntime="false">
|
||||
<classpath refid="libraries" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="${bin}" />
|
||||
<delete dir="${doc}" />
|
||||
</target>
|
||||
|
||||
<target name="rebuild" depends="clean, build" />
|
||||
|
||||
<target name="test" depends="build">
|
||||
<junit fork="true" haltonfailure="true">
|
||||
<classpath refid="master" />
|
||||
<formatter type="plain" usefile="false" />
|
||||
<batchtest>
|
||||
<fileset refid="tests" />
|
||||
</batchtest>
|
||||
</junit>
|
||||
</target>
|
||||
|
||||
<target name="doc" depends="build">
|
||||
<javadoc sourcepath="${src}" classpathref="libraries" access="private" destdir="${doc}" windowtitle="Apollo">
|
||||
<doclet name="org.jboss.apiviz.APIviz" pathref="libraries">
|
||||
<param name="-sourceclasspath" value="${bin}" />
|
||||
<param name="-author" />
|
||||
<param name="-version" />
|
||||
<param name="-use" />
|
||||
<param name="-nopackagediagram" />
|
||||
</doclet>
|
||||
<doctitle><![CDATA[<h1>Apollo</h1>]]></doctitle>
|
||||
<link href="http://download.oracle.com/javase/6/docs/api/" />
|
||||
<link href="http://docs.jboss.org/netty/3.2/api/" />
|
||||
<link href="http://guava-libraries.googlecode.com/svn/trunk/javadoc/" />
|
||||
<link href="http://www.junit.org/apidocs/" />
|
||||
<link href="http://commons.apache.org/compress/apidocs/" />
|
||||
<link href="http://jruby.org/apidocs/" />
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
<target name="run" depends="build">
|
||||
<java classpathref="master" fork="true" classname="org.apollo.Server">
|
||||
<arg value="org.apollo.net.release.r${version}.Release${version}" />
|
||||
<jvmarg value="-Xbootclasspath/a:${lib}/jsr166-1.0.0.jar" />
|
||||
</java>
|
||||
</target>
|
||||
</project>
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
<events>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.KeepAliveEvent</type>
|
||||
<chain />
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.CharacterDesignEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.CharacterDesignVerificationHandler</handler>
|
||||
<handler>org.apollo.game.event.handler.impl.CharacterDesignEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.WalkEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.WalkEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.ChatEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.ChatVerificationHandler</handler>
|
||||
<handler>org.apollo.game.event.handler.impl.ChatEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.ButtonEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.BankButtonEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.CommandEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.CommandEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.SwitchItemEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.SwitchItemEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.ObjectActionEvent</type>
|
||||
<chain />
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.EquipEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.EquipEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.ItemActionEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.RemoveEventHandler</handler>
|
||||
<handler>org.apollo.game.event.handler.impl.BankEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.ClosedInterfaceEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.ClosedInterfaceEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
<event>
|
||||
<type>org.apollo.game.event.impl.EnteredAmountEvent</type>
|
||||
<chain>
|
||||
<handler>org.apollo.game.event.handler.impl.EnteredAmountEventHandler</handler>
|
||||
</chain>
|
||||
</event>
|
||||
</events>
|
||||
@@ -0,0 +1,4 @@
|
||||
<login>
|
||||
<loader>org.apollo.io.player.impl.BinaryPlayerLoader</loader>
|
||||
<saver>org.apollo.io.player.impl.BinaryPlayerSaver</saver>
|
||||
</login>
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.action.DistancedAction'
|
||||
java_import 'org.apollo.game.model.inter.bank.BankUtils'
|
||||
|
||||
BANK_BOOTH_ID = 2213
|
||||
BANK_BOOTH_SIZE = 1
|
||||
|
||||
class BankAction < DistancedAction
|
||||
attr_reader :position
|
||||
|
||||
def initialize(character, position)
|
||||
super 0, true, character, position, BANK_BOOTH_SIZE
|
||||
@position = position
|
||||
end
|
||||
|
||||
def executeAction
|
||||
character.turn_to @position
|
||||
BankUtils.open_bank character
|
||||
stop
|
||||
end
|
||||
|
||||
def equals(other)
|
||||
return (get_class == other.get_class and @position == other.position)
|
||||
end
|
||||
end
|
||||
|
||||
on :event, :object_action do |ctx, player, event|
|
||||
if event.option == 2 and event.id == BANK_BOOTH_ID
|
||||
player.startAction BankAction.new(player, event.position)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: when we have NPCs/dialogue, also respond to clicking on them
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>bank</id>
|
||||
<version>1</version>
|
||||
<name>Bank</name>
|
||||
<description>Opens the bank interface when players select 'use-quickly' on a bank booth.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>bank.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,167 @@
|
||||
# A script to 'bootstrap' all of the other plugins, wrapping Apollo's verbose
|
||||
# Java-style API in a Ruby-style API.
|
||||
#
|
||||
# Written by Graham.
|
||||
|
||||
# ********************************** WARNING **********************************
|
||||
# * If you do not really understand what this is for, do not edit it without *
|
||||
# * creating a backup! Many plugins rely on the behaviour of this script, and *
|
||||
# * will break if you mess it up. *
|
||||
# * *
|
||||
# * This is actually part of the core server and in an ideal world shouldn't *
|
||||
# * be changed. *
|
||||
# *****************************************************************************
|
||||
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.event.handler.EventHandler'
|
||||
java_import 'org.apollo.game.command.PrivilegedCommandListener'
|
||||
java_import 'org.apollo.game.model.Player'
|
||||
java_import 'org.apollo.game.model.World'
|
||||
java_import 'org.apollo.game.scheduling.ScheduledTask'
|
||||
|
||||
# Alias the privilege levels.
|
||||
RIGHTS_ADMIN = Player::PrivilegeLevel::ADMINISTRATOR
|
||||
RIGHTS_MOD = Player::PrivilegeLevel::MODERATOR
|
||||
RIGHTS_STANDARD = Player::PrivilegeLevel::STANDARD
|
||||
|
||||
# Extends the (Ruby) String class with a method to convert a lower case,
|
||||
# underscore delimited string to camel-case.
|
||||
class String
|
||||
def camelize
|
||||
gsub(/(?:^|_)(.)/) { $1.upcase }
|
||||
end
|
||||
end
|
||||
|
||||
# A CommandListener which executes a Proc object with two arguments: the player
|
||||
# and the command.
|
||||
class ProcCommandListener < PrivilegedCommandListener
|
||||
def initialize(rights, block)
|
||||
super rights
|
||||
@block = block
|
||||
end
|
||||
|
||||
def executePrivileged(player, command)
|
||||
@block.call player, command
|
||||
end
|
||||
end
|
||||
|
||||
# An EventHandler which executes a Proc object with three arguments: the chain
|
||||
# context, the player and the event.
|
||||
class ProcEventHandler < EventHandler
|
||||
def initialize(block)
|
||||
super() # required (with brackets!), see http://jira.codehaus.org/browse/JRUBY-679
|
||||
@block = block
|
||||
end
|
||||
|
||||
def handle(ctx, player, event)
|
||||
@block.call ctx, player, event
|
||||
end
|
||||
end
|
||||
|
||||
# A ScheduledTask which executes a Proc object with one argument (itself).
|
||||
class ProcScheduledTask < ScheduledTask
|
||||
def initialize(delay, immediate, block)
|
||||
super delay, immediate
|
||||
@block = block
|
||||
end
|
||||
|
||||
def execute
|
||||
@block.call self
|
||||
end
|
||||
end
|
||||
|
||||
# Schedules a ScheduledTask. Can be used in two ways: passing an existing
|
||||
# ScheduledTask object or passing a block along with one or two parameters: the
|
||||
# delay (in pulses) and, optionally, the immediate flag.
|
||||
#
|
||||
# If the immediate flag is not given, it defaults to false.
|
||||
#
|
||||
# The ScheduledTask object is passed to the block so that methods such as
|
||||
# setDelay and stop can be called. execute MUST NOT be called - if it is, the
|
||||
# behaviour is undefined (and most likely it'll be bad).
|
||||
def schedule(*args, &block)
|
||||
if block_given?
|
||||
if args.length == 1 or args.length == 2
|
||||
delay = args[0]
|
||||
immediate = args.length == 2 ? args[1] : false
|
||||
World.world.schedule ProcScheduledTask.new(delay, immediate, block)
|
||||
else
|
||||
raise "invalid combination of arguments"
|
||||
end
|
||||
elsif args.length == 1
|
||||
World.world.schedule args[0]
|
||||
else
|
||||
raise "invalid combination of arguments"
|
||||
end
|
||||
end
|
||||
|
||||
# Defines some sort of action to take upon an event. The following 'kinds' of
|
||||
# event are currently valid:
|
||||
#
|
||||
# * :command
|
||||
# * :event
|
||||
# * :button
|
||||
#
|
||||
# A command takes one or two arguments (the command name and optionally the
|
||||
# minimum rights level to use it). The minimum rights level defaults to
|
||||
# STANDARD. The block should have two arguments: player and command.
|
||||
#
|
||||
# An event takes no arguments. The block should have three arguments: the chain
|
||||
# context, the player and the event object.
|
||||
#
|
||||
# A button takes one argument (the id). The block should have one argument: the
|
||||
# player who clicked the button.
|
||||
def on(kind, *args, &block)
|
||||
case kind
|
||||
when :command then on_command(args, block)
|
||||
when :event then on_event(args, block)
|
||||
when :button then on_button(args, block)
|
||||
else raise "unknown event type"
|
||||
end
|
||||
end
|
||||
|
||||
# Defines an action to be taken upon a button press.
|
||||
def on_button(args, proc)
|
||||
if args.length != 1
|
||||
raise "button must have one argument"
|
||||
end
|
||||
|
||||
id = args[0].to_i
|
||||
|
||||
on :event, :button do |ctx, player, event|
|
||||
if event.interface_id == id
|
||||
proc.call player
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Defines an action to be taken upon an event.
|
||||
# The event can either be a symbol with the lower case, underscored class name
|
||||
# or the class itself.
|
||||
def on_event(args, proc)
|
||||
if args.length != 1
|
||||
raise "event must have one argument"
|
||||
end
|
||||
|
||||
evt = args[0]
|
||||
if evt.is_a? Symbol
|
||||
class_name = evt.to_s.camelize.concat "Event"
|
||||
evt = Java::JavaClass.for_name("org.apollo.game.event.impl.".concat class_name)
|
||||
end
|
||||
|
||||
$ctx.add_last_event_handler evt, ProcEventHandler.new(proc)
|
||||
end
|
||||
|
||||
# Defines an action to be taken upon a command.
|
||||
def on_command(args, proc)
|
||||
if args.length != 1 and args.length != 2
|
||||
raise "command event must have one or two arguments"
|
||||
end
|
||||
|
||||
rights = RIGHTS_STANDARD
|
||||
if args.length == 2
|
||||
rights = args[1]
|
||||
end
|
||||
|
||||
$ctx.add_command_listener args[0].to_s, ProcCommandListener.new(rights, proc)
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.model.inter.bank.BankUtils'
|
||||
|
||||
on :command, :bank, RIGHTS_ADMIN do |player, command|
|
||||
BankUtils.open_bank player
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>cmd-bank</id>
|
||||
<version>1</version>
|
||||
<name>Bank Command</name>
|
||||
<description>Adds a ::bank command.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>bank.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,27 @@
|
||||
on :command, :item, RIGHTS_ADMIN do |player, command|
|
||||
args = command.arguments
|
||||
if (1..2).include? args.length
|
||||
id = args[0].to_i
|
||||
amount = args.length == 2 ? args[1].to_i : 1
|
||||
|
||||
player.inventory.add id, amount
|
||||
else
|
||||
player.send_message "Syntax: ::item [id] [amount=1]"
|
||||
end
|
||||
end
|
||||
|
||||
on :command, :destroy, RIGHTS_ADMIN do |player, command|
|
||||
args = command.arguments
|
||||
if (1..2).include? args.length
|
||||
id = args[0].to_i
|
||||
amount = args.length == 2 ? args[1].to_i : 1
|
||||
|
||||
player.inventory.remove id, amount
|
||||
else
|
||||
player.send_message "Syntax: ::destroy [id] [amount=1]"
|
||||
end
|
||||
end
|
||||
|
||||
on :command, :empty, RIGHTS_ADMIN do |player, command|
|
||||
player.inventory.clear
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>cmd-item</id>
|
||||
<version>1</version>
|
||||
<name>Item Commands</name>
|
||||
<description>Adds ::item, ::destroy and ::empty commands.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>item.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>cmd-skill</id>
|
||||
<version>1</version>
|
||||
<name>Skill Commands</name>
|
||||
<description>Adds a ::max command.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>skill.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,9 @@
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.model.SkillSet'
|
||||
|
||||
on :command, :max, RIGHTS_ADMIN do |player, command|
|
||||
skills = player.skill_set
|
||||
(0...skills.size).each do |skill|
|
||||
skills.add_experience(skill, SkillSet::MAXIMUM_EXP)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>cmd-teleport</id>
|
||||
<version>1</version>
|
||||
<name>Teleport Commands</name>
|
||||
<description>Adds ::pos and ::tele commands.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>teleport.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,19 @@
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.model.Position'
|
||||
|
||||
on :command, :pos, RIGHTS_ADMIN do |player, command|
|
||||
player.send_message "You are at: " + player.position.to_s
|
||||
end
|
||||
|
||||
on :command, :tele, RIGHTS_ADMIN do |player, command|
|
||||
args = command.arguments
|
||||
if (2..3).include? args.length
|
||||
x = args[0].to_i
|
||||
y = args[1].to_i
|
||||
z = args.length == 3 ? args[2].to_i : 0
|
||||
|
||||
player.teleport Position.new(x, y, z)
|
||||
else
|
||||
player.send_message "Syntax: ::tele [x] [y] [z=0]"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.action.DistancedAction'
|
||||
java_import 'org.apollo.game.model.Animation'
|
||||
java_import 'org.apollo.game.model.Skill'
|
||||
|
||||
# TODO: this shouldn't use the punch animation/delay, but should use the one
|
||||
# from the active weapon/attack style (according to Scu11 anyway).
|
||||
|
||||
DUMMY_ID = 823
|
||||
DUMMY_SIZE = 1
|
||||
PUNCH_ANIMATION = 422
|
||||
ANIMATION_PULSES = 0 # TODO: might be more if hitting it actually works instead of showing 'nothing more' msg?
|
||||
LEVEL_THRESHOLD = 8
|
||||
EXP_PER_HIT = 5
|
||||
|
||||
class DummyAction < DistancedAction
|
||||
attr_reader :position
|
||||
|
||||
def initialize(character, position)
|
||||
super ANIMATION_PULSES, true, character, position, DUMMY_SIZE
|
||||
|
||||
@position = position
|
||||
@started = false
|
||||
end
|
||||
|
||||
def executeAction
|
||||
if not @started
|
||||
@started = true
|
||||
|
||||
character.send_message "You hit the dummy."
|
||||
character.turn_to @position
|
||||
character.play_animation Animation.new(PUNCH_ANIMATION)
|
||||
else
|
||||
skills = character.skill_set
|
||||
|
||||
if skills.skill(Skill::ATTACK).maximum_level >= LEVEL_THRESHOLD then
|
||||
character.send_message "There is nothing more you can learn from hitting a dummy."
|
||||
else
|
||||
skills.add_experience Skill::ATTACK, EXP_PER_HIT
|
||||
end
|
||||
|
||||
stop
|
||||
end
|
||||
end
|
||||
|
||||
def equals(other)
|
||||
return (get_class == other.get_class and @position == other.position)
|
||||
end
|
||||
end
|
||||
|
||||
on :event, :object_action do |ctx, player, event|
|
||||
if event.option == 2 and event.id == DUMMY_ID
|
||||
player.start_action DummyAction.new(player, event.position)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>dummy</id>
|
||||
<version>1</version>
|
||||
<name>Dummies</name>
|
||||
<description>Allows low-level players to train on dummies.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>dummy.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,7 @@
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.model.Animation'
|
||||
|
||||
on :command, :hello do |player, command|
|
||||
player.play_animation Animation::WAVE
|
||||
player.send_message "Hello, World!"
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>hello</id>
|
||||
<version>1</version>
|
||||
<name>Hello World Command</name>
|
||||
<description>Adds a ::hello command which simply prints 'Hello, World!'.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>hello.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,5 @@
|
||||
LOGOUT_BUTTON_ID = 2458
|
||||
|
||||
on :button, LOGOUT_BUTTON_ID do |player|
|
||||
player.logout
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>logout</id>
|
||||
<version>1</version>
|
||||
<name>Logout Button</name>
|
||||
<description>Adds the logout button.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>logout.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,19 @@
|
||||
GEMS = {}
|
||||
|
||||
class Gem
|
||||
attr_reader :id, :chance
|
||||
|
||||
def initialize(id, chance)
|
||||
@id = id
|
||||
@chance = chance
|
||||
end
|
||||
end
|
||||
|
||||
def append_gem(gem)
|
||||
GEMS[gem.id] = gem
|
||||
end
|
||||
|
||||
append_gem(Gem.new(1623, 0)) # uncut sapphire
|
||||
append_gem(Gem.new(1605, 0)) # uncut emerald
|
||||
append_gem(Gem.new(1619, 0)) # uncut ruby
|
||||
append_gem(Gem.new(1617, 0)) # uncut diamond
|
||||
@@ -0,0 +1,163 @@
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.action.DistancedAction'
|
||||
java_import 'org.apollo.game.model.EquipmentConstants'
|
||||
java_import 'org.apollo.game.model.def.ItemDefinition'
|
||||
|
||||
PROSPECT_PULSES = 3
|
||||
ORE_SIZE = 1
|
||||
|
||||
# TODO: finish implementing this
|
||||
class MiningAction < DistancedAction
|
||||
attr_reader :position, :ore, :counter, :started
|
||||
|
||||
def initialize(character, position, ore)
|
||||
super 0, true, character, position, ORE_SIZE
|
||||
@position = position
|
||||
@ore = ore
|
||||
@started = false
|
||||
@counter = 0
|
||||
end
|
||||
|
||||
def find_pickaxe
|
||||
PICKAXE_IDS.each do |id|
|
||||
weapon = character.equipment.get EquipmentConstants::WEAPON
|
||||
if weapon.id == id
|
||||
return PICKAXES[id]
|
||||
end
|
||||
|
||||
if character.inventory.contains id
|
||||
return PICKAXES[id]
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
# starts the mining animation, sets counters/flags and turns the character to
|
||||
# the ore
|
||||
def start_mine(pickaxe)
|
||||
@started = true
|
||||
character.send_message "You swing your pick at the rock."
|
||||
character.turn_to @position
|
||||
character.play_animation pickaxe.animation
|
||||
@counter = pickaxe.pulses
|
||||
end
|
||||
|
||||
def executeAction
|
||||
skills = character.skill_set
|
||||
level = skills.get_skill(Skill::MINING).maximum_level # TODO: is using max level correct?
|
||||
pickaxe = find_pickaxe
|
||||
|
||||
# verify the player can mine with their pickaxe
|
||||
if not (pickaxe != nil and level >= pickaxe.level)
|
||||
character.send_message "You do not have a pickaxe for which you have the level to use."
|
||||
stop
|
||||
return
|
||||
end
|
||||
|
||||
# verify the player can mine the ore
|
||||
if ore.level > level
|
||||
character.send_message "You do not have the required level to mine this rock."
|
||||
stop
|
||||
return
|
||||
end
|
||||
|
||||
# check if we need to kick start things
|
||||
if not @started
|
||||
start_mine(pickaxe)
|
||||
else
|
||||
# count down and check if we can have a chance at some ore now
|
||||
if @counter == 0
|
||||
# TODO: calculate the chance that the player can actually get the rock
|
||||
|
||||
if character.inventory.add ore.id
|
||||
ore_def = ItemDefinition.for_id @ore.id # TODO: split off into some method
|
||||
name = ore_def.name.sub(/ ore$/, "").downcase
|
||||
|
||||
character.send_message "You manage to mine some #{name}."
|
||||
skills.add_experience Skill::MINING, ore.exp
|
||||
# TODO: expire the rock
|
||||
end
|
||||
|
||||
stop
|
||||
end
|
||||
@counter -= 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def equals(other)
|
||||
return (get_class == other.get_class and @position == other.position and @ore == other.ore)
|
||||
end
|
||||
end
|
||||
|
||||
class ExpiredProspectingAction < DistancedAction
|
||||
attr_reader :position
|
||||
|
||||
def initialize(character, position)
|
||||
super 0, true, character, position, ORE_SIZE
|
||||
end
|
||||
|
||||
def executeAction
|
||||
character.send_message "There is currently no ore available in this rock."
|
||||
stop
|
||||
end
|
||||
|
||||
def equals(other)
|
||||
return (get_class == other.get_class and @position == other.position)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class ProspectingAction < DistancedAction
|
||||
attr_reader :position, :ore
|
||||
|
||||
def initialize(character, position, ore)
|
||||
super PROSPECT_PULSES, true, character, position, ORE_SIZE
|
||||
@position = position
|
||||
@ore = ore
|
||||
@started = false
|
||||
end
|
||||
|
||||
def executeAction
|
||||
if not @started
|
||||
@started = true
|
||||
|
||||
character.send_message "You examine the rock for ores..."
|
||||
character.turn_to @position
|
||||
else
|
||||
ore_def = ItemDefinition.for_id @ore.id
|
||||
name = ore_def.name.sub(/ ore$/, "").downcase
|
||||
|
||||
character.send_message "This rock contains #{name}."
|
||||
|
||||
stop
|
||||
end
|
||||
end
|
||||
|
||||
def equals(other)
|
||||
return (get_class == other.get_class and @position == other.position and @ore == other.ore)
|
||||
end
|
||||
end
|
||||
|
||||
on :event, :object_action do |ctx, player, event|
|
||||
if event.option == 1
|
||||
ore = ORES[event.id]
|
||||
if ore != nil
|
||||
player.startAction MiningAction.new(player, event.position, ore)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
on :event, :object_action do |ctx, player, event|
|
||||
if event.option == 2
|
||||
ore = ORES[event.id]
|
||||
if ore != nil
|
||||
player.startAction ProspectingAction.new(player, event.position, ore)
|
||||
else
|
||||
if EXPIRED_ORES[event.id] != nil
|
||||
player.startAction ExpiredProspectingAction.new(player, event.position)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,96 @@
|
||||
# Thanks to Mikey` <http://www.rune-server.org/members/mikey%60/> for helping
|
||||
# to find some of the item/object IDs, minimum levels and experiences.
|
||||
#
|
||||
# Thanks to Clifton <http://www.rune-server.org/members/clifton/> for helping
|
||||
# to find some of the expired object IDs.
|
||||
|
||||
ORES = {}
|
||||
EXPIRED_ORES = {}
|
||||
|
||||
class Ore
|
||||
attr_reader :id, :objects, :level, :exp, :respawn
|
||||
|
||||
def initialize(id, objects, level, exp, respawn)
|
||||
@id = id
|
||||
@objects = objects
|
||||
@level = level
|
||||
@exp = exp
|
||||
@respawn = respawn
|
||||
end
|
||||
end
|
||||
|
||||
def append_ore(ore)
|
||||
ore.objects.each do |obj, expired_obj|
|
||||
ORES[obj] = ore
|
||||
EXPIRED_ORES[expired_obj] = true
|
||||
end
|
||||
end
|
||||
|
||||
CLAY_OBJECTS = {
|
||||
2180 => 450 , 2109 => 451 , 14904 => 14896, 14905 => 14897
|
||||
}
|
||||
|
||||
COPPER_OBJECTS = {
|
||||
11960 => 11555, 11961 => 11556, 11962 => 11557, 11936 => 11552,
|
||||
11937 => 11553, 11938 => 11554, 2090 => 450 , 2091 => 451 ,
|
||||
14906 => 14898, 14907 => 14899, 14856 => 14832, 14857 => 14833,
|
||||
14858 => 14834
|
||||
}
|
||||
|
||||
TIN_OBJECTS = {
|
||||
11597 => 11555, 11958 => 11556, 11959 => 11557, 11933 => 11552,
|
||||
11934 => 11553, 11935 => 11554, 2094 => 450 , 2095 => 451 ,
|
||||
14092 => 14894, 14903 => 14895
|
||||
}
|
||||
|
||||
IRON_OBJECTS = {
|
||||
11954 => 11555, 11955 => 11556, 11956 => 11557, 2092 => 450 ,
|
||||
2093 => 451 , 14900 => 14892, 14901 => 14893, 14913 => 14915,
|
||||
14914 => 14916
|
||||
}
|
||||
|
||||
COAL_OBJECTS = {
|
||||
11963 => 11555, 11964 => 11556, 11965 => 11557, 11930 => 11552,
|
||||
11931 => 11553, 11932 => 11554, 2096 => 450 , 2097 => 451 ,
|
||||
14850 => 14832, 14851 => 14833, 14852 => 14834
|
||||
}
|
||||
|
||||
SILVER_OBJECTS = {
|
||||
11948 => 11555, 11949 => 11556, 11950 => 11557, 2100 => 450 ,
|
||||
2101 => 451
|
||||
}
|
||||
|
||||
GOLD_OBJECTS = {
|
||||
11951 => 11555, 11952 => 11556, 11953 => 11557, 2098 => 450 ,
|
||||
2099 => 451
|
||||
}
|
||||
|
||||
MITHRIL_OBJECTS = {
|
||||
11945 => 11555, 11946 => 11556, 11947 => 11557, 11942 => 11552,
|
||||
11943 => 11553, 11944 => 11554, 2102 => 450 , 2103 => 451 ,
|
||||
14853 => 14832, 14854 => 14833, 14855 => 14834
|
||||
}
|
||||
|
||||
ADAMANT_OBJECTS = {
|
||||
11939 => 11552, 11940 => 11553, 11941 => 11554, 2104 => 450 ,
|
||||
2105 => 451 , 14862 => 14832, 14863 => 14833, 14864 => 14834
|
||||
}
|
||||
|
||||
RUNITE_OBJECTS = {
|
||||
2106 => 450 , 2107 => 451 , 14859 => 14832, 14860 => 14833,
|
||||
14861 => 14834
|
||||
}
|
||||
|
||||
append_ore Ore.new(434, CLAY_OBJECTS, 1, 5, 3 ) # clay
|
||||
append_ore Ore.new(436, COPPER_OBJECTS, 1, 17.5, 6 ) # copper ore
|
||||
append_ore Ore.new(438, TIN_OBJECTS, 1, 17.5, 6 ) # tin ore
|
||||
append_ore Ore.new(440, IRON_OBJECTS, 15, 35, 16 ) # iron ore
|
||||
append_ore Ore.new(453, COAL_OBJECTS, 30, 50, 100 ) # coal
|
||||
append_ore Ore.new(444, GOLD_OBJECTS, 40, 65, 200 ) # gold ore
|
||||
append_ore Ore.new(442, SILVER_OBJECTS, 20, 40, 200 ) # silver ore
|
||||
append_ore Ore.new(447, MITHRIL_OBJECTS, 55, 80, 400 ) # mithril ore
|
||||
append_ore Ore.new(449, ADAMANT_OBJECTS, 70, 95, 800 ) # adamant ore
|
||||
append_ore Ore.new(451, RUNITE_OBJECTS, 85, 125, 2500) # runite ore
|
||||
|
||||
# TODO: rune essence object id = 2491
|
||||
# level 1, exp 5, rune ess = 1436, pure ess = 7936
|
||||
@@ -0,0 +1,31 @@
|
||||
require 'java'
|
||||
java_import 'org.apollo.game.model.Animation'
|
||||
|
||||
PICKAXES = {}
|
||||
PICKAXE_IDS = []
|
||||
|
||||
class Pickaxe
|
||||
attr_reader :id, :level, :animation, :pulses
|
||||
|
||||
def initialize(id, level, animation, pulses)
|
||||
@id = id
|
||||
@level = level
|
||||
@animation = Animation.new(animation)
|
||||
@pulses = pulses
|
||||
end
|
||||
end
|
||||
|
||||
def append_pickaxe(pickaxe)
|
||||
PICKAXES[pickaxe.id] = pickaxe
|
||||
PICKAXE_IDS << pickaxe.id # tacky way of keeping things in order
|
||||
end
|
||||
|
||||
# NOTE: ADD LOWER LEVEL PICKAXES FIRST
|
||||
append_pickaxe(Pickaxe.new(1265, 1, 625, 8)) # bronze pickaxe
|
||||
append_pickaxe(Pickaxe.new(1267, 1, 626, 7)) # iron pickaxe
|
||||
append_pickaxe(Pickaxe.new(1269, 1, 627, 6)) # steel pickaxe
|
||||
append_pickaxe(Pickaxe.new(1273, 21, 629, 5)) # mithril pickaxe
|
||||
append_pickaxe(Pickaxe.new(1271, 31, 628, 4)) # adamant pickaxe
|
||||
append_pickaxe(Pickaxe.new(1275, 41, 624, 3)) # rune pickaxe
|
||||
|
||||
PICKAXE_IDS.reverse!
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0"?>
|
||||
<plugin>
|
||||
<id>mining</id>
|
||||
<version>1</version>
|
||||
<name>Mining</name>
|
||||
<description>Adds the mining skill.</description>
|
||||
<authors>
|
||||
<author>Graham</author>
|
||||
<author>Mikey`</author>
|
||||
<author>WH:II:DOW</author>
|
||||
<author>Requa</author>
|
||||
<author>Clifton</author>
|
||||
</authors>
|
||||
<scripts>
|
||||
<script>pickaxe.rb</script>
|
||||
<script>ore.rb</script>
|
||||
<script>gem.rb</script>
|
||||
<script>respawn.rb</script>
|
||||
<script>mining.rb</script>
|
||||
</scripts>
|
||||
<dependencies />
|
||||
</plugin>
|
||||
@@ -0,0 +1,18 @@
|
||||
MAX_PLAYERS = 2000 # TODO: obtain from a Java class
|
||||
|
||||
# Calculates the number of pulses it takes for an ore to respawn based on the
|
||||
# number of players currently online.
|
||||
#
|
||||
# The 'base' argument is the number of pulses it takes with no players online.
|
||||
# The 'players' argument is the number of players currently logged into the
|
||||
# current world.
|
||||
#
|
||||
# The base times can be found on this website:
|
||||
# http://runescape.salmoneus.net/mining.html#respawn
|
||||
#
|
||||
# These must be converted to pulses (seconds * 10 / 6) to work with this
|
||||
# function. The rest of the mining plugin rounds the base respawn times in
|
||||
# pulses down where appropriate.
|
||||
def respawn_pulses(base, players)
|
||||
base - players * base / (MAX_PLAYERS * 2)
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
<services>
|
||||
<service>org.apollo.game.GameService</service>
|
||||
<service>org.apollo.login.LoginService</service>
|
||||
<service>org.apollo.update.UpdateService</service>
|
||||
</services>
|
||||
@@ -0,0 +1,3 @@
|
||||
<synchronizer>
|
||||
<active>org.apollo.game.sync.ParallelClientSynchronizer</active>
|
||||
</synchronizer>
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ivy-module version="2.0">
|
||||
<info organisation="org.apollo" module="apollo" />
|
||||
<dependencies>
|
||||
<dependency org="org.jboss.netty" name="netty" rev="3.2.6.Final" />
|
||||
<dependency org="org.jruby" name="jruby-complete" rev="1.6.5" />
|
||||
<dependency org="junit" name="junit" rev="4.10" />
|
||||
<dependency org="com.google.guava" name="guava" rev="10.0.1" />
|
||||
<dependency org="org.apache.commons" name="commons-compress" rev="1.1" />
|
||||
<dependency org="java.util.concurrent" name="jsr166" rev="1.0.0" />
|
||||
<dependency org="org.jboss.apiviz" name="apiviz" rev="1.3.1.GA" />
|
||||
</dependencies>
|
||||
</ivy-module>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ivysettings>
|
||||
<settings defaultResolver="resolvers" />
|
||||
<resolvers>
|
||||
<chain name="resolvers">
|
||||
<filesystem name="local">
|
||||
<artifact pattern="${root}/etc/[module].[ext]" />
|
||||
</filesystem>
|
||||
<url name="maven-r1" m2compatible="true">
|
||||
<artifact pattern="http://repo1.maven.org/maven2/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
|
||||
</url>
|
||||
<url name="maven-r2" m2compatible="true">
|
||||
<artifact pattern="http://repo2.maven.org/maven2/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
|
||||
</url>
|
||||
<url name="jboss" m2compatible="true">
|
||||
<artifact pattern="http://repository.jboss.org/nexus/content/groups/public-jboss/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
|
||||
</url>
|
||||
<url name="jboss-legacy" m2compatible="true">
|
||||
<artifact pattern="http://repository.jboss.org/maven2/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
|
||||
</url>
|
||||
</chain>
|
||||
</resolvers>
|
||||
</ivysettings>
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
package net.burtleburtle.bob.rand;
|
||||
|
||||
/**
|
||||
* <p>An implementation of the
|
||||
* <a href="http://www.burtleburtle.net/bob/rand/isaacafa.html">ISAAC</a>
|
||||
* psuedorandom number generator.</p>
|
||||
* <pre>
|
||||
* ------------------------------------------------------------------------------
|
||||
* Rand.java: By Bob Jenkins. My random number generator, ISAAC.
|
||||
* rand.init() -- initialize
|
||||
* rand.val() -- get a random value
|
||||
* MODIFIED:
|
||||
* 960327: Creation (addition of randinit, really)
|
||||
* 970719: use context, not global variables, for internal state
|
||||
* 980224: Translate to Java
|
||||
* ------------------------------------------------------------------------------
|
||||
* </pre>
|
||||
* <p>This class has been changed to be more conformant to Java and javadoc
|
||||
* conventions.</p>
|
||||
* @author Bob Jenkins
|
||||
*/
|
||||
public final class IsaacRandom {
|
||||
|
||||
/**
|
||||
* The golden ratio.
|
||||
*/
|
||||
private static final int GOLDEN_RATIO = 0x9e3779b9;
|
||||
|
||||
/**
|
||||
* The log of the size of the result and memory arrays.
|
||||
*/
|
||||
private static final int SIZEL = 8;
|
||||
|
||||
/**
|
||||
* The size of the result and memory arrays.
|
||||
*/
|
||||
private static final int SIZE = 1 << SIZEL;
|
||||
|
||||
/**
|
||||
* A mask for pseudorandom lookup.
|
||||
*/
|
||||
private static int MASK = (SIZE - 1) << 2;
|
||||
|
||||
/**
|
||||
* The count through the results in the results array.
|
||||
*/
|
||||
private int count;
|
||||
|
||||
/**
|
||||
* The results given to the user.
|
||||
*/
|
||||
private int[] rsl;
|
||||
|
||||
/**
|
||||
* The internal state.
|
||||
*/
|
||||
private int[] mem;
|
||||
|
||||
/**
|
||||
* The accumulator.
|
||||
*/
|
||||
private int a;
|
||||
|
||||
/**
|
||||
* The last result.
|
||||
*/
|
||||
private int b;
|
||||
|
||||
/**
|
||||
* The counter.
|
||||
*/
|
||||
private int c;
|
||||
|
||||
/**
|
||||
* Creates the random number generator without an initial seed.
|
||||
*/
|
||||
public IsaacRandom() {
|
||||
mem = new int[SIZE];
|
||||
rsl = new int[SIZE];
|
||||
init(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the random number generator with the specified seed.
|
||||
* @param seed The seed.
|
||||
*/
|
||||
public IsaacRandom(int[] seed) {
|
||||
mem = new int[SIZE];
|
||||
rsl = new int[SIZE];
|
||||
for (int i = 0; i < seed.length; ++i) {
|
||||
rsl[i] = seed[i];
|
||||
}
|
||||
init(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 256 results.
|
||||
*/
|
||||
private void isaac() {
|
||||
int i, j, x, y;
|
||||
|
||||
b += ++c;
|
||||
for (i = 0, j = SIZE / 2; i < SIZE / 2;) {
|
||||
x = mem[i];
|
||||
a ^= a << 13;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a >>> 6;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a << 2;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a >>> 16;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
}
|
||||
|
||||
for (j = 0; j < SIZE / 2;) {
|
||||
x = mem[i];
|
||||
a ^= a << 13;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a >>> 6;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a << 2;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
|
||||
x = mem[i];
|
||||
a ^= a >>> 16;
|
||||
a += mem[j++];
|
||||
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
|
||||
rsl[i++] = b = mem[((y >> SIZEL) & MASK) >> 2] + x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises this random number generator.
|
||||
* @param flag Set to {@code true} if a seed was passed to the constructor.
|
||||
*/
|
||||
private void init(boolean flag) {
|
||||
int i;
|
||||
int a, b, c, d, e, f, g, h;
|
||||
a = b = c = d = e = f = g = h = GOLDEN_RATIO;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
a ^= b << 11;
|
||||
d += a;
|
||||
b += c;
|
||||
b ^= c >>> 2;
|
||||
e += b;
|
||||
c += d;
|
||||
c ^= d << 8;
|
||||
f += c;
|
||||
d += e;
|
||||
d ^= e >>> 16;
|
||||
g += d;
|
||||
e += f;
|
||||
e ^= f << 10;
|
||||
h += e;
|
||||
f += g;
|
||||
f ^= g >>> 4;
|
||||
a += f;
|
||||
g += h;
|
||||
g ^= h << 8;
|
||||
b += g;
|
||||
h += a;
|
||||
h ^= a >>> 9;
|
||||
c += h;
|
||||
a += b;
|
||||
}
|
||||
|
||||
for (i = 0; i < SIZE; i += 8) { /* fill in mem[] with messy stuff */
|
||||
if (flag) {
|
||||
a += rsl[i];
|
||||
b += rsl[i + 1];
|
||||
c += rsl[i + 2];
|
||||
d += rsl[i + 3];
|
||||
e += rsl[i + 4];
|
||||
f += rsl[i + 5];
|
||||
g += rsl[i + 6];
|
||||
h += rsl[i + 7];
|
||||
}
|
||||
a ^= b << 11;
|
||||
d += a;
|
||||
b += c;
|
||||
b ^= c >>> 2;
|
||||
e += b;
|
||||
c += d;
|
||||
c ^= d << 8;
|
||||
f += c;
|
||||
d += e;
|
||||
d ^= e >>> 16;
|
||||
g += d;
|
||||
e += f;
|
||||
e ^= f << 10;
|
||||
h += e;
|
||||
f += g;
|
||||
f ^= g >>> 4;
|
||||
a += f;
|
||||
g += h;
|
||||
g ^= h << 8;
|
||||
b += g;
|
||||
h += a;
|
||||
h ^= a >>> 9;
|
||||
c += h;
|
||||
a += b;
|
||||
mem[i] = a;
|
||||
mem[i + 1] = b;
|
||||
mem[i + 2] = c;
|
||||
mem[i + 3] = d;
|
||||
mem[i + 4] = e;
|
||||
mem[i + 5] = f;
|
||||
mem[i + 6] = g;
|
||||
mem[i + 7] = h;
|
||||
}
|
||||
|
||||
if (flag) { /* second pass makes all of seed affect all of mem */
|
||||
for (i = 0; i < SIZE; i += 8) {
|
||||
a += mem[i];
|
||||
b += mem[i + 1];
|
||||
c += mem[i + 2];
|
||||
d += mem[i + 3];
|
||||
e += mem[i + 4];
|
||||
f += mem[i + 5];
|
||||
g += mem[i + 6];
|
||||
h += mem[i + 7];
|
||||
a ^= b << 11;
|
||||
d += a;
|
||||
b += c;
|
||||
b ^= c >>> 2;
|
||||
e += b;
|
||||
c += d;
|
||||
c ^= d << 8;
|
||||
f += c;
|
||||
d += e;
|
||||
d ^= e >>> 16;
|
||||
g += d;
|
||||
e += f;
|
||||
e ^= f << 10;
|
||||
h += e;
|
||||
f += g;
|
||||
f ^= g >>> 4;
|
||||
a += f;
|
||||
g += h;
|
||||
g ^= h << 8;
|
||||
b += g;
|
||||
h += a;
|
||||
h ^= a >>> 9;
|
||||
c += h;
|
||||
a += b;
|
||||
mem[i] = a;
|
||||
mem[i + 1] = b;
|
||||
mem[i + 2] = c;
|
||||
mem[i + 3] = d;
|
||||
mem[i + 4] = e;
|
||||
mem[i + 5] = f;
|
||||
mem[i + 6] = g;
|
||||
mem[i + 7] = h;
|
||||
}
|
||||
}
|
||||
|
||||
isaac();
|
||||
count = SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next random value.
|
||||
* @return The next random value.
|
||||
*/
|
||||
public int nextInt() {
|
||||
if (0 == count--) {
|
||||
isaac();
|
||||
count = SIZE - 1;
|
||||
}
|
||||
return rsl[count];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains a Java implementation of Bob Jenkins' ISAAC algorithm.
|
||||
*/
|
||||
package net.burtleburtle.bob.rand;
|
||||
@@ -0,0 +1,177 @@
|
||||
package org.apollo;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apollo.fs.IndexedFileSystem;
|
||||
import org.apollo.game.model.World;
|
||||
import org.apollo.net.ApolloHandler;
|
||||
import org.apollo.net.HttpPipelineFactory;
|
||||
import org.apollo.net.JagGrabPipelineFactory;
|
||||
import org.apollo.net.ServicePipelineFactory;
|
||||
import org.apollo.net.NetworkConstants;
|
||||
import org.apollo.net.release.Release;
|
||||
import org.apollo.net.release.r317.Release317;
|
||||
import org.apollo.util.plugin.PluginContext;
|
||||
import org.apollo.util.plugin.PluginManager;
|
||||
import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||
import org.jboss.netty.channel.ChannelFactory;
|
||||
import org.jboss.netty.channel.ChannelPipelineFactory;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
import org.jboss.netty.util.HashedWheelTimer;
|
||||
import org.jboss.netty.util.Timer;
|
||||
|
||||
/**
|
||||
* The core class of the Apollo server.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Server {
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(Server.class.getName());
|
||||
|
||||
/**
|
||||
* The entry point of the Apollo server application.
|
||||
* @param args The command-line arguments passed to the application.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Server server = null;
|
||||
try {
|
||||
server = new Server();
|
||||
server.init(args.length == 1 ? args[0] : Release317.class.getName());
|
||||
|
||||
SocketAddress service = new InetSocketAddress(NetworkConstants.SERVICE_PORT);
|
||||
SocketAddress http = new InetSocketAddress(NetworkConstants.HTTP_PORT);
|
||||
SocketAddress jaggrab = new InetSocketAddress(NetworkConstants.JAGGRAB_PORT);
|
||||
|
||||
server.start();
|
||||
server.bind(service, http, jaggrab);
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "Error whilst starting server.", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ServerBootstrap} for the service listener.
|
||||
*/
|
||||
private final ServerBootstrap serviceBootstrap = new ServerBootstrap();
|
||||
|
||||
/**
|
||||
* The {@link ServerBootstrap} for the HTTP listener.
|
||||
*/
|
||||
private final ServerBootstrap httpBootstrap = new ServerBootstrap();
|
||||
|
||||
/**
|
||||
* The {@link ServerBootstrap} for the JAGGRAB listener.
|
||||
*/
|
||||
private final ServerBootstrap jagGrabBootstrap = new ServerBootstrap();
|
||||
|
||||
/**
|
||||
* The {@link ExecutorService} used for network events. The named thread
|
||||
* factory is unused as Netty names threads itself.
|
||||
*/
|
||||
private final ExecutorService networkExecutor = Executors.newCachedThreadPool();
|
||||
|
||||
/**
|
||||
* The service manager.
|
||||
*/
|
||||
private final ServiceManager serviceManager;
|
||||
|
||||
/**
|
||||
* The timer used for idle checking.
|
||||
*/
|
||||
private final Timer timer = new HashedWheelTimer();
|
||||
|
||||
/**
|
||||
* The server's context.
|
||||
*/
|
||||
private ServerContext context;
|
||||
|
||||
/**
|
||||
* Creates the Apollo server.
|
||||
* @throws Exception if an error occurs whilst creating services.
|
||||
*/
|
||||
public Server() throws Exception {
|
||||
logger.info("Starting Apollo...");
|
||||
serviceManager = new ServiceManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the server.
|
||||
* @param releaseClassName The class name of the current active
|
||||
* {@link Release}.
|
||||
* @throws ClassNotFoundException if the release class could not be found.
|
||||
* @throws IllegalAccessException if the release class could not be accessed.
|
||||
* @throws InstantiationException if the release class could not be instantiated.
|
||||
*/
|
||||
public void init(String releaseClassName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||
Class<?> clazz = Class.forName(releaseClassName);
|
||||
Release release = (Release) clazz.newInstance();
|
||||
|
||||
logger.info("Initialized release #" + release.getReleaseNumber() + ".");
|
||||
|
||||
ChannelFactory factory = new NioServerSocketChannelFactory(networkExecutor, networkExecutor);
|
||||
serviceBootstrap.setFactory(factory);
|
||||
httpBootstrap.setFactory(factory);
|
||||
jagGrabBootstrap.setFactory(factory);
|
||||
|
||||
context = new ServerContext(release, serviceManager);
|
||||
ApolloHandler handler = new ApolloHandler(context);
|
||||
|
||||
ChannelPipelineFactory servicePipelineFactory = new ServicePipelineFactory(handler, timer);
|
||||
serviceBootstrap.setPipelineFactory(servicePipelineFactory);
|
||||
|
||||
ChannelPipelineFactory httpPipelineFactory = new HttpPipelineFactory(handler, timer);
|
||||
httpBootstrap.setPipelineFactory(httpPipelineFactory);
|
||||
|
||||
ChannelPipelineFactory jagGrabPipelineFactory = new JagGrabPipelineFactory(handler, timer);
|
||||
jagGrabBootstrap.setPipelineFactory(jagGrabPipelineFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the server to the specified address.
|
||||
* @param serviceAddress The service address to bind to.
|
||||
* @param httpAddress The HTTP address to bind to.
|
||||
* @param jagGrabAddress The JAGGRAB address to bind to.
|
||||
*/
|
||||
public void bind(SocketAddress serviceAddress, SocketAddress httpAddress, SocketAddress jagGrabAddress) {
|
||||
logger.info("Binding service listener to address: " + serviceAddress + "...");
|
||||
serviceBootstrap.bind(serviceAddress);
|
||||
|
||||
logger.info("Binding HTTP listener to address: " + httpAddress + "...");
|
||||
try {
|
||||
httpBootstrap.bind(httpAddress);
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.WARNING, "Binding to HTTP failed: client will use JAGGRAB as a fallback (not reccomended)!", t);
|
||||
}
|
||||
|
||||
logger.info("Binding JAGGRAB listener to address: " + jagGrabAddress + "...");
|
||||
jagGrabBootstrap.bind(jagGrabAddress);
|
||||
|
||||
logger.info("Ready for connections.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server.
|
||||
* @throws Exception if an error occurs.
|
||||
*/
|
||||
public void start() throws Exception {
|
||||
PluginManager mgr = new PluginManager(new PluginContext(context));
|
||||
mgr.start(); // TODO move this?
|
||||
|
||||
serviceManager.startAll();
|
||||
|
||||
// TODO move this?
|
||||
int releaseNo = context.getRelease().getReleaseNumber();
|
||||
IndexedFileSystem fs = new IndexedFileSystem(new File("data/fs/" + releaseNo), true);
|
||||
World.getWorld().init(releaseNo, fs, mgr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.apollo;
|
||||
|
||||
import org.apollo.net.release.Release;
|
||||
import org.jboss.netty.channel.group.ChannelGroup;
|
||||
import org.jboss.netty.channel.group.DefaultChannelGroup;
|
||||
|
||||
/**
|
||||
* A {@link ServerContext} is created along with the {@link Server} object. The
|
||||
* primary difference is that a reference to the current context should be
|
||||
* passed around within the server. The {@link Server} should not be as it
|
||||
* allows access to some methods such as
|
||||
* {@link Server#bind(java.net.SocketAddress, java.net.SocketAddress, java.net.SocketAddress)}
|
||||
* which user scripts/code should not be able to access.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ServerContext {
|
||||
|
||||
/**
|
||||
* The current release.
|
||||
*/
|
||||
private final Release release;
|
||||
|
||||
/**
|
||||
* The service manager.
|
||||
*/
|
||||
private final ServiceManager serviceManager;
|
||||
|
||||
/**
|
||||
* The channel group.
|
||||
*/
|
||||
private final ChannelGroup group = new DefaultChannelGroup();
|
||||
|
||||
/**
|
||||
* Creates a new server context.
|
||||
* @param release The current release.
|
||||
* @param serviceManager The service manager.
|
||||
*/
|
||||
ServerContext(Release release, ServiceManager serviceManager) {
|
||||
this.release = release;
|
||||
this.serviceManager = serviceManager;
|
||||
this.serviceManager.setContext(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the channel group.
|
||||
* @return The channel group.
|
||||
*/
|
||||
public ChannelGroup getChannelGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current release.
|
||||
* @return The current release.
|
||||
*/
|
||||
public Release getRelease() {
|
||||
return release;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the service manager.
|
||||
* @return The service manager.
|
||||
*/
|
||||
public ServiceManager getServiceManager() {
|
||||
return serviceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a service. This method is shorthand for
|
||||
* {@code getServiceManager().getService(...)}.
|
||||
* @param <S> The type of service.
|
||||
* @param clazz The service class.
|
||||
* @return The service, or {@code null} if it could not be found.
|
||||
*/
|
||||
public <S extends Service> S getService(Class<S> clazz) {
|
||||
return serviceManager.getService(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.apollo;
|
||||
|
||||
/**
|
||||
* Represents a service that the server provides.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class Service {
|
||||
|
||||
/**
|
||||
* The server context.
|
||||
*/
|
||||
private ServerContext ctx;
|
||||
|
||||
/**
|
||||
* Gets the server context.
|
||||
* @return The context.
|
||||
*/
|
||||
public final ServerContext getContext() {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server context.
|
||||
* @param ctx The context.
|
||||
*/
|
||||
public final void setContext(ServerContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the service.
|
||||
*/
|
||||
public abstract void start();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.apollo;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apollo.util.xml.XmlNode;
|
||||
import org.apollo.util.xml.XmlParser;
|
||||
|
||||
/**
|
||||
* A class which manages {@link Service}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ServiceManager {
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(ServiceManager.class.getName());
|
||||
|
||||
/**
|
||||
* The service map.
|
||||
*/
|
||||
private Map<Class<? extends Service>, Service> services = new HashMap<Class<? extends Service>, Service>();
|
||||
|
||||
/**
|
||||
* Creates and initializes the {@link ServiceManager}.
|
||||
* @throws Exception if an error occurs.
|
||||
*/
|
||||
public ServiceManager() throws Exception {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this service manager.
|
||||
* @throws Exception if an error occurs.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void init() throws Exception {
|
||||
logger.info("Registering services...");
|
||||
|
||||
XmlParser parser = new XmlParser();
|
||||
XmlNode rootNode;
|
||||
|
||||
InputStream is = new FileInputStream("data/services.xml");
|
||||
try {
|
||||
rootNode = parser.parse(is);
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
|
||||
if (!rootNode.getName().equals("services")) {
|
||||
throw new Exception("unexpected name of root node");
|
||||
}
|
||||
|
||||
for (XmlNode childNode : rootNode) {
|
||||
if (!childNode.getName().equals("service")) {
|
||||
throw new Exception("unexpected name of child node");
|
||||
}
|
||||
|
||||
if (!childNode.hasValue()) {
|
||||
throw new Exception("child node must have a value!");
|
||||
}
|
||||
|
||||
Class<? extends Service> clazz = (Class<? extends Service>) Class.forName(childNode.getValue());
|
||||
register((Class<Service>) clazz, clazz.newInstance());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a service.
|
||||
* @param <S> The type of service.
|
||||
* @param clazz The service's class.
|
||||
* @param service The service.
|
||||
*/
|
||||
private <S extends Service> void register(Class<S> clazz, S service) {
|
||||
logger.fine("Registering service: " + clazz + "...");
|
||||
services.put(clazz, service);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a service.
|
||||
* @param <S> The type of service.
|
||||
* @param clazz The service class.
|
||||
* @return The service.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends Service> S getService(Class<S> clazz) {
|
||||
return (S) services.get(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts all the services.
|
||||
*/
|
||||
public void startAll() {
|
||||
logger.info("Starting services...");
|
||||
for (Service service : services.values()) {
|
||||
logger.fine("Starting service: " + service.getClass().getName() + "...");
|
||||
service.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of all services.
|
||||
* @param ctx The server context.
|
||||
*/
|
||||
public void setContext(ServerContext ctx) {
|
||||
for (Service s : services.values()) {
|
||||
s.setContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.apollo.fs;
|
||||
|
||||
/**
|
||||
* A class which points to a file in the cache.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class FileDescriptor {
|
||||
|
||||
/**
|
||||
* The file type.
|
||||
*/
|
||||
private final int type;
|
||||
|
||||
/**
|
||||
* The file id.
|
||||
*/
|
||||
private final int file;
|
||||
|
||||
/**
|
||||
* Creates the file descriptor.
|
||||
* @param type The file type.
|
||||
* @param file The file id.
|
||||
*/
|
||||
public FileDescriptor(int type, int file) {
|
||||
this.type = type;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file type.
|
||||
* @return The file type.
|
||||
*/
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file id.
|
||||
* @return The file id.
|
||||
*/
|
||||
public int getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.apollo.fs;
|
||||
|
||||
/**
|
||||
* Holds file system related constants.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class FileSystemConstants {
|
||||
|
||||
/**
|
||||
* The number of caches.
|
||||
*/
|
||||
public static final int CACHE_COUNT = 5;
|
||||
|
||||
/**
|
||||
* The number of archives in cache 0.
|
||||
*/
|
||||
public static final int ARCHIVE_COUNT = 9;
|
||||
|
||||
/**
|
||||
* The size of an index.
|
||||
*/
|
||||
public static final int INDEX_SIZE = 6;
|
||||
|
||||
/**
|
||||
* The size of a header.
|
||||
*/
|
||||
public static final int HEADER_SIZE = 8;
|
||||
|
||||
/**
|
||||
* The size of a chunk.
|
||||
*/
|
||||
public static final int CHUNK_SIZE = 512;
|
||||
|
||||
/**
|
||||
* The size of a block.
|
||||
*/
|
||||
public static final int BLOCK_SIZE = HEADER_SIZE + CHUNK_SIZE;
|
||||
|
||||
/**
|
||||
* Default private constructor to prevent instantiation.
|
||||
*/
|
||||
private FileSystemConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.apollo.fs;
|
||||
|
||||
/**
|
||||
* An {@link Index} points to a file in the {@code main_file_cache.dat} file.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Index {
|
||||
|
||||
/**
|
||||
* Decodes a buffer into an index.
|
||||
* @param buffer The buffer.
|
||||
* @return The decoded {@link Index}.
|
||||
* @throws IllegalArgumentException if the buffer length is invalid.
|
||||
*/
|
||||
public static Index decode(byte[] buffer) {
|
||||
if (buffer.length != FileSystemConstants.INDEX_SIZE) {
|
||||
throw new IllegalArgumentException("Incorrect buffer length.");
|
||||
}
|
||||
|
||||
int size = ((buffer[0] & 0xFF) << 16) | ((buffer[1] & 0xFF) << 8) | (buffer[2] & 0xFF);
|
||||
int block = ((buffer[3] & 0xFF) << 16) | ((buffer[4] & 0xFF) << 8) | (buffer[5] & 0xFF);
|
||||
|
||||
return new Index(size, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the file.
|
||||
*/
|
||||
private final int size;
|
||||
|
||||
/**
|
||||
* The first block of the file.
|
||||
*/
|
||||
private final int block;
|
||||
|
||||
/**
|
||||
* Creates the index.
|
||||
* @param size The size of the file.
|
||||
* @param block The first block of the file.
|
||||
*/
|
||||
public Index(int size, int block) {
|
||||
this.size = size;
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the file.
|
||||
* @return The size of the file.
|
||||
*/
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first block of the file.
|
||||
* @return The first block of the file.
|
||||
*/
|
||||
public int getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
package org.apollo.fs;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
/**
|
||||
* A file system based on top of the operating system's file system. It
|
||||
* consists of a data file and index files. Index files point to blocks in the
|
||||
* data file, which contains the actual data.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class IndexedFileSystem implements Closeable {
|
||||
|
||||
/**
|
||||
* Read only flag.
|
||||
*/
|
||||
private final boolean readOnly;
|
||||
|
||||
/**
|
||||
* The index files.
|
||||
*/
|
||||
private RandomAccessFile[] indices = new RandomAccessFile[256];
|
||||
|
||||
/**
|
||||
* The data file.
|
||||
*/
|
||||
private RandomAccessFile data;
|
||||
|
||||
/**
|
||||
* The cached CRC table.
|
||||
*/
|
||||
private ByteBuffer crcTable;
|
||||
|
||||
/**
|
||||
* Creates the file system with the specified base directory.
|
||||
* @param base The base directory.
|
||||
* @param readOnly A flag indicating if the file system will be read only.
|
||||
* @throws Exception if the file system is invalid.
|
||||
*/
|
||||
public IndexedFileSystem(File base, boolean readOnly) throws Exception {
|
||||
this.readOnly = readOnly;
|
||||
detectLayout(base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this {@link IndexedFileSystem} is read only.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically detect the layout of the specified directory.
|
||||
* @param base The base directory.
|
||||
* @throws Exception if the file system is invalid.
|
||||
*/
|
||||
private void detectLayout(File base) throws Exception {
|
||||
int indexCount = 0;
|
||||
for (int index = 0; index < indices.length; index++) {
|
||||
File f = new File(base.getAbsolutePath() + "/main_file_cache.idx" + index);
|
||||
if (f.exists() && !f.isDirectory()) {
|
||||
indexCount++;
|
||||
indices[index] = new RandomAccessFile(f, readOnly ? "r" : "rw");
|
||||
}
|
||||
}
|
||||
if (indexCount <= 0) {
|
||||
throw new Exception("No index file(s) present");
|
||||
}
|
||||
|
||||
File oldEngineData = new File(base.getAbsolutePath() + "/main_file_cache.dat");
|
||||
File newEngineData = new File(base.getAbsolutePath() + "/main_file_cache.dat2");
|
||||
if (oldEngineData.exists() && !oldEngineData.isDirectory()) {
|
||||
data = new RandomAccessFile(oldEngineData, readOnly ? "r" : "rw");
|
||||
} else if (newEngineData.exists() && !oldEngineData.isDirectory()) {
|
||||
data = new RandomAccessFile(newEngineData, readOnly ? "r" : "rw");
|
||||
} else {
|
||||
throw new Exception("No data file present");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a file.
|
||||
* @param fd The {@link FileDescriptor} which points to the file.
|
||||
* @return The {@link Index}.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private Index getIndex(FileDescriptor fd) throws IOException {
|
||||
int index = fd.getType();
|
||||
if (index < 0 || index >= indices.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[FileSystemConstants.INDEX_SIZE];
|
||||
RandomAccessFile indexFile = indices[index];
|
||||
synchronized (indexFile) {
|
||||
long ptr = (long) fd.getFile() * (long) FileSystemConstants.INDEX_SIZE;
|
||||
if (ptr >= 0 && indexFile.length() >= (ptr + FileSystemConstants.INDEX_SIZE)) {
|
||||
indexFile.seek(ptr);
|
||||
indexFile.readFully(buffer);
|
||||
} else {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
return Index.decode(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of files with the specified type.
|
||||
* @param type The type.
|
||||
* @return The number of files.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private int getFileCount(int type) throws IOException {
|
||||
if (type < 0 || type >= indices.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
RandomAccessFile indexFile = indices[type];
|
||||
synchronized (indexFile) {
|
||||
return (int) (indexFile.length() / FileSystemConstants.INDEX_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CRC table.
|
||||
* @return The CRC table.
|
||||
* @throws IOException if an I/O erorr occurs.
|
||||
*/
|
||||
public ByteBuffer getCrcTable() throws IOException {
|
||||
if (readOnly) {
|
||||
synchronized (this) {
|
||||
if (crcTable != null) {
|
||||
return crcTable.duplicate();
|
||||
}
|
||||
}
|
||||
|
||||
// the number of archives
|
||||
int archives = getFileCount(0);
|
||||
|
||||
// the hash
|
||||
int hash = 1234;
|
||||
|
||||
// the CRCs
|
||||
int[] crcs = new int[archives];
|
||||
|
||||
// calculate the CRCs
|
||||
CRC32 crc32 = new CRC32();
|
||||
for (int i = 1; i < crcs.length; i++) {
|
||||
crc32.reset();
|
||||
|
||||
ByteBuffer bb = getFile(0, i);
|
||||
byte[] bytes = new byte[bb.remaining()];
|
||||
bb.get(bytes, 0, bytes.length);
|
||||
crc32.update(bytes, 0, bytes.length);
|
||||
|
||||
crcs[i] = (int) crc32.getValue();
|
||||
}
|
||||
|
||||
// hash the CRCs and place them in the buffer
|
||||
ByteBuffer buf = ByteBuffer.allocate(crcs.length * 4 + 4);
|
||||
for (int i = 0; i < crcs.length; i++) {
|
||||
hash = (hash << 1) + crcs[i];
|
||||
buf.putInt(crcs[i]);
|
||||
}
|
||||
|
||||
// place the hash into the buffer
|
||||
buf.putInt(hash);
|
||||
buf.flip();
|
||||
|
||||
synchronized (this) {
|
||||
crcTable = buf.asReadOnlyBuffer();
|
||||
return crcTable.duplicate();
|
||||
}
|
||||
} else {
|
||||
throw new IOException("cannot get CRC table from a writable file system");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file.
|
||||
* @param type The file type.
|
||||
* @param file The file id.
|
||||
* @return A {@link ByteBuffer} which contains the contents of the file.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public ByteBuffer getFile(int type, int file) throws IOException {
|
||||
return getFile(new FileDescriptor(type, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file.
|
||||
* @param fd The {@link FileDescriptor} which points to the file.
|
||||
* @return A {@link ByteBuffer} which contains the contents of the file.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public ByteBuffer getFile(FileDescriptor fd) throws IOException {
|
||||
Index index = getIndex(fd);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(index.getSize());
|
||||
|
||||
// calculate some initial values
|
||||
long ptr = (long) index.getBlock() * (long) FileSystemConstants.BLOCK_SIZE;
|
||||
int read = 0;
|
||||
int size = index.getSize();
|
||||
int blocks = size / FileSystemConstants.CHUNK_SIZE;
|
||||
if (size % FileSystemConstants.CHUNK_SIZE != 0) {
|
||||
blocks++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < blocks; i++) {
|
||||
|
||||
// read header
|
||||
byte[] header = new byte[FileSystemConstants.HEADER_SIZE];
|
||||
synchronized (data) {
|
||||
data.seek(ptr);
|
||||
data.readFully(header);
|
||||
}
|
||||
|
||||
// increment pointers
|
||||
ptr += FileSystemConstants.HEADER_SIZE;
|
||||
|
||||
// parse header
|
||||
int nextFile = ((header[0] & 0xFF) << 8) | (header[1] & 0xFF);
|
||||
int curChunk = ((header[2] & 0xFF) << 8) | (header[3] & 0xFF);
|
||||
int nextBlock = ((header[4] & 0xFF) << 16) | ((header[5] & 0xFF) << 8) | (header[6] & 0xFF);
|
||||
int nextType = header[7] & 0xFF;
|
||||
|
||||
// check expected chunk id is correct
|
||||
if (i != curChunk) {
|
||||
throw new IOException("Chunk id mismatch.");
|
||||
}
|
||||
|
||||
// calculate how much we can read
|
||||
int chunkSize = size - read;
|
||||
if (chunkSize > FileSystemConstants.CHUNK_SIZE) {
|
||||
chunkSize = FileSystemConstants.CHUNK_SIZE;
|
||||
}
|
||||
|
||||
// read the next chunk and put it in the buffer
|
||||
byte[] chunk = new byte[chunkSize];
|
||||
synchronized (data) {
|
||||
data.seek(ptr);
|
||||
data.readFully(chunk);
|
||||
}
|
||||
buffer.put(chunk);
|
||||
|
||||
// increment pointers
|
||||
read += chunkSize;
|
||||
ptr = (long) nextBlock * (long) FileSystemConstants.BLOCK_SIZE;
|
||||
|
||||
// if we still have more data to read, check the validity of the
|
||||
// header
|
||||
if (size > read) {
|
||||
if (nextType != (fd.getType() + 1)) {
|
||||
throw new IOException("File type mismatch.");
|
||||
}
|
||||
|
||||
if (nextFile != fd.getFile()) {
|
||||
throw new IOException("File id mismatch.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (data != null) {
|
||||
synchronized (data) {
|
||||
data.close();
|
||||
}
|
||||
}
|
||||
|
||||
for (RandomAccessFile index : indices) {
|
||||
if (index != null) {
|
||||
synchronized (index) {
|
||||
index.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.apollo.fs.archive;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.apollo.util.ByteBufferUtil;
|
||||
import org.apollo.util.CompressionUtil;
|
||||
|
||||
/**
|
||||
* Represents an archive.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Archive {
|
||||
|
||||
/**
|
||||
* Decodes the archive in the specified buffer.
|
||||
* @param buffer The buffer.
|
||||
* @return The archive.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public static Archive decode(ByteBuffer buffer) throws IOException {
|
||||
int extractedSize = ByteBufferUtil.readUnsignedTriByte(buffer);
|
||||
int size = ByteBufferUtil.readUnsignedTriByte(buffer);
|
||||
boolean extracted = false;
|
||||
|
||||
if (size != extractedSize) {
|
||||
byte[] compressed = new byte[size];
|
||||
byte[] uncompressed = new byte[extractedSize];
|
||||
buffer.get(compressed);
|
||||
CompressionUtil.unbzip2(compressed, uncompressed);
|
||||
buffer = ByteBuffer.wrap(uncompressed);
|
||||
extracted = true;
|
||||
}
|
||||
|
||||
int entries = buffer.getShort() & 0xFFFF;
|
||||
int[] identifiers = new int[entries];
|
||||
int[] extractedSizes = new int[entries];
|
||||
int[] sizes = new int[entries];
|
||||
|
||||
for (int i = 0; i < entries; i++) {
|
||||
identifiers[i] = buffer.getInt();
|
||||
extractedSizes[i] = ByteBufferUtil.readUnsignedTriByte(buffer);
|
||||
sizes[i] = ByteBufferUtil.readUnsignedTriByte(buffer);
|
||||
}
|
||||
|
||||
ArchiveEntry[] entry = new ArchiveEntry[entries];
|
||||
|
||||
for (int i = 0; i < entries; i++) {
|
||||
ByteBuffer entryBuffer = ByteBuffer.allocate(extractedSizes[i]);
|
||||
if (!extracted) {
|
||||
byte[] compressed = new byte[sizes[i]];
|
||||
byte[] uncompressed = new byte[extractedSizes[i]];
|
||||
buffer.get(compressed);
|
||||
CompressionUtil.unbzip2(compressed, uncompressed);
|
||||
entryBuffer = ByteBuffer.wrap(uncompressed);
|
||||
}
|
||||
entry[i] = new ArchiveEntry(identifiers[i], entryBuffer);
|
||||
}
|
||||
|
||||
return new Archive(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* The entries in this archive.
|
||||
*/
|
||||
private final ArchiveEntry[] entries;
|
||||
|
||||
/**
|
||||
* Creates a new archive.
|
||||
* @param entries The entries in this archive.
|
||||
*/
|
||||
public Archive(ArchiveEntry[] entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entry by its name.
|
||||
* @param name The name.
|
||||
* @return The entry.
|
||||
* @throws FileNotFoundException if the file could not be found.
|
||||
*/
|
||||
public ArchiveEntry getEntry(String name) throws FileNotFoundException {
|
||||
int hash = 0;
|
||||
name = name.toUpperCase();
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
hash = (hash * 61 + name.charAt(i)) - 32;
|
||||
}
|
||||
for (ArchiveEntry entry : entries) {
|
||||
if (entry.getIdentifier() == hash) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.apollo.fs.archive;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Represents a single entry in an {@link Archive}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ArchiveEntry {
|
||||
|
||||
/**
|
||||
* The identifier of this entry.
|
||||
*/
|
||||
private final int identifier;
|
||||
|
||||
/**
|
||||
* The buffer of this entry.
|
||||
*/
|
||||
private final ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* Creates a new archive entry.
|
||||
* @param identifier The identifier.
|
||||
* @param buffer The buffer.
|
||||
*/
|
||||
public ArchiveEntry(int identifier, ByteBuffer buffer) {
|
||||
this.identifier = identifier;
|
||||
this.buffer = buffer.asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identifier of this entry.
|
||||
* @return The identifier of this entry.
|
||||
*/
|
||||
public int getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the buffer of this entry.
|
||||
* @return This buffer of this entry.
|
||||
*/
|
||||
public ByteBuffer getBuffer() {
|
||||
return buffer.duplicate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes which deal with archives.
|
||||
*/
|
||||
package org.apollo.fs.archive;
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Contains classes which deal with the file system that the client uses to
|
||||
* store game data files.
|
||||
*/
|
||||
package org.apollo.fs;
|
||||
@@ -0,0 +1,186 @@
|
||||
package org.apollo.fs.parser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.apollo.fs.IndexedFileSystem;
|
||||
import org.apollo.fs.archive.Archive;
|
||||
import org.apollo.game.model.def.ItemDefinition;
|
||||
import org.apollo.util.ByteBufferUtil;
|
||||
|
||||
/**
|
||||
* A class which parses item definitions.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ItemDefinitionParser {
|
||||
|
||||
/**
|
||||
* The indexed file system.
|
||||
*/
|
||||
private final IndexedFileSystem fs;
|
||||
|
||||
/**
|
||||
* Creates the item definition parser.
|
||||
* @param fs The indexed file system.
|
||||
*/
|
||||
public ItemDefinitionParser(IndexedFileSystem fs) {
|
||||
this.fs = fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the item definitions.
|
||||
* @return The item definitions.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public ItemDefinition[] parse() throws IOException {
|
||||
Archive config = Archive.decode(fs.getFile(0, 2));
|
||||
ByteBuffer dat = config.getEntry("obj.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("obj.idx").getBuffer();
|
||||
|
||||
int count = idx.getShort();
|
||||
int[] indices = new int[count];
|
||||
int index = 2;
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices[i] = index;
|
||||
index += idx.getShort();
|
||||
}
|
||||
|
||||
ItemDefinition[] defs = new ItemDefinition[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
dat.position(indices[i]);
|
||||
defs[i] = parseDefinition(i, dat);
|
||||
}
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single definition.
|
||||
* @param id The item's id.
|
||||
* @param buffer The buffer.
|
||||
* @return The definition.
|
||||
*/
|
||||
private ItemDefinition parseDefinition(int id, ByteBuffer buffer) {
|
||||
ItemDefinition def = new ItemDefinition(id);
|
||||
|
||||
while (true) {
|
||||
int code = buffer.get() & 0xFF;
|
||||
|
||||
if (code == 0) {
|
||||
return def;
|
||||
} else if (code == 1) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelId = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 2) {
|
||||
def.setName(ByteBufferUtil.readString(buffer));
|
||||
} else if (code == 3) {
|
||||
def.setDescription(ByteBufferUtil.readString(buffer));
|
||||
} else if (code == 4) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelScale = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 5) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelRotationX = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 6) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelRotationY = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 7) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelTransformationX = buffer.getShort();
|
||||
} else if (code == 8) {
|
||||
@SuppressWarnings("unused")
|
||||
int modelTransformationY = buffer.getShort();
|
||||
} else if (code == 10) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 11) {
|
||||
def.setStackable(true);
|
||||
} else if (code == 12) {
|
||||
def.setValue(buffer.getInt());
|
||||
} else if (code == 16) {
|
||||
def.setMembersOnly(true);
|
||||
} else if (code == 23) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknownShort = buffer.getShort() & 0xFFFF;
|
||||
@SuppressWarnings("unused")
|
||||
int unknownByte = buffer.get();
|
||||
} else if (code == 24) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknownShort = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 25) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknownShort = buffer.getShort() & 0xFFFF;
|
||||
@SuppressWarnings("unused")
|
||||
int unknownByte = buffer.get();
|
||||
} else if (code == 26) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknownShort = buffer.getShort() & 0xFFFF;
|
||||
} else if (code >= 30 && code < 35) {
|
||||
String str = ByteBufferUtil.readString(buffer);
|
||||
if (str.equalsIgnoreCase("hidden")) {
|
||||
str = null;
|
||||
}
|
||||
def.setGroundAction(code - 30, str);
|
||||
} else if (code >= 35 && code < 40) {
|
||||
String str = ByteBufferUtil.readString(buffer);
|
||||
def.setInventoryAction(code - 35, str);
|
||||
} else if (code == 40) {
|
||||
int colorCount = buffer.get() & 0xFF;
|
||||
for (int i = 0; i < colorCount; i++) {
|
||||
@SuppressWarnings("unused")
|
||||
int oldColor = buffer.getShort() & 0xFFFF;
|
||||
@SuppressWarnings("unused")
|
||||
int newColor = buffer.getShort() & 0xFFFF;
|
||||
}
|
||||
} else if (code == 78) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 79) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 90) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 91) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 92) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 93) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 95) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 97) {
|
||||
int noteInfoId = buffer.getShort() & 0xFFFF;
|
||||
def.setNoteInfoId(noteInfoId);
|
||||
} else if (code == 98) {
|
||||
int noteGraphicId = buffer.getShort() & 0xFFFF;
|
||||
def.setNoteGraphicId(noteGraphicId);
|
||||
} else if (code >= 100 && code < 110) {
|
||||
@SuppressWarnings("unused")
|
||||
int stackId = buffer.getShort() & 0xFFFF;
|
||||
@SuppressWarnings("unused")
|
||||
int stackAmount = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 110) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 111) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 112) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() & 0xFFFF;
|
||||
} else if (code == 114) {
|
||||
@SuppressWarnings("unused")
|
||||
int unknown = buffer.getShort() * 5;
|
||||
} else if (code == 115) {
|
||||
@SuppressWarnings("unused")
|
||||
int team = buffer.get() & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes which parse files within the game's cache.
|
||||
*/
|
||||
package org.apollo.fs.parser;
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.apollo.game;
|
||||
|
||||
/**
|
||||
* Contains game-related constants.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class GameConstants {
|
||||
|
||||
/**
|
||||
* The delay between consecutive pulses, in milliseconds.
|
||||
*/
|
||||
public static final int PULSE_DELAY = 600;
|
||||
|
||||
/**
|
||||
* The maximum events per pulse per session.
|
||||
*/
|
||||
public static final int EVENTS_PER_PULSE = 10;
|
||||
|
||||
/**
|
||||
* Default private constructor to prevent instantiation by other classes.
|
||||
*/
|
||||
private GameConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.apollo.game;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A class which handles the logic for each pulse of the {@link GameService}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class GamePulseHandler implements Runnable {
|
||||
|
||||
/**
|
||||
* The logger for this class.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(GamePulseHandler.class.getName());
|
||||
|
||||
/**
|
||||
* The {@link GameService}.
|
||||
*/
|
||||
private final GameService service;
|
||||
|
||||
/**
|
||||
* Creates the game pulse handler object.
|
||||
* @param service The {@link GameService}.
|
||||
*/
|
||||
GamePulseHandler(GameService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
service.pulse();
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "Exception during pulse.", t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package org.apollo.game;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apollo.Service;
|
||||
import org.apollo.game.event.handler.chain.EventHandlerChainGroup;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.World;
|
||||
import org.apollo.game.model.World.RegistrationStatus;
|
||||
import org.apollo.game.sync.ClientSynchronizer;
|
||||
import org.apollo.io.EventHandlerChainParser;
|
||||
import org.apollo.login.LoginService;
|
||||
import org.apollo.net.session.GameSession;
|
||||
import org.apollo.util.NamedThreadFactory;
|
||||
import org.apollo.util.xml.XmlNode;
|
||||
import org.apollo.util.xml.XmlParser;
|
||||
|
||||
/**
|
||||
* The {@link GameService} class schedules and manages the execution of the
|
||||
* {@link GamePulseHandler} class.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class GameService extends Service {
|
||||
|
||||
/**
|
||||
* The number of times to unregister players per cycle. This is to ensure
|
||||
* the saving threads don't get swamped with requests and slow everything
|
||||
* down.
|
||||
*/
|
||||
private static final int UNREGISTERS_PER_CYCLE = 50;
|
||||
|
||||
/**
|
||||
* The scheduled executor service.
|
||||
*/
|
||||
private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("GameService"));
|
||||
|
||||
/**
|
||||
* A queue of players to remove.
|
||||
*/
|
||||
private final Queue<Player> oldPlayers = new ConcurrentLinkedQueue<Player>();
|
||||
|
||||
/**
|
||||
* The {@link EventHandlerChainGroup}.
|
||||
*/
|
||||
private EventHandlerChainGroup chainGroup;
|
||||
|
||||
/**
|
||||
* The {@link ClientSynchronizer}.
|
||||
*/
|
||||
private ClientSynchronizer synchronizer;
|
||||
|
||||
/**
|
||||
* Creates the game service.
|
||||
* @throws Exception if an error occurs during initialization.
|
||||
*/
|
||||
public GameService() throws Exception {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event handler chains.
|
||||
* @return The event handler chains.
|
||||
*/
|
||||
public EventHandlerChainGroup getEventHandlerChains() {
|
||||
return chainGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the game service.
|
||||
* @throws Exception if an error occurs.
|
||||
*/
|
||||
private void init() throws Exception {
|
||||
InputStream is = new FileInputStream("data/events.xml");
|
||||
try {
|
||||
EventHandlerChainParser chainGroupParser = new EventHandlerChainParser(is);
|
||||
chainGroup = chainGroupParser.parse();
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
|
||||
is = new FileInputStream("data/synchronizer.xml");
|
||||
try {
|
||||
XmlParser parser = new XmlParser();
|
||||
XmlNode rootNode = parser.parse(is);
|
||||
|
||||
if (!rootNode.getName().equals("synchronizer")) {
|
||||
throw new Exception("Invalid root node name.");
|
||||
}
|
||||
|
||||
XmlNode activeNode = rootNode.getChild("active");
|
||||
if (activeNode == null || !activeNode.hasValue()) {
|
||||
throw new Exception("No active node/value.");
|
||||
}
|
||||
|
||||
Class<?> clazz = Class.forName(activeNode.getValue());
|
||||
synchronizer = (ClientSynchronizer) clazz.newInstance();
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the game service.
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
scheduledExecutor.scheduleAtFixedRate(new GamePulseHandler(this), GameConstants.PULSE_DELAY, GameConstants.PULSE_DELAY, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every pulse.
|
||||
*/
|
||||
public void pulse() {
|
||||
synchronized (this) {
|
||||
LoginService loginService = getContext().getService(LoginService.class);
|
||||
World world = World.getWorld();
|
||||
|
||||
int unregistered = 0;
|
||||
Player old;
|
||||
while (unregistered < UNREGISTERS_PER_CYCLE && (old = oldPlayers.poll()) != null) {
|
||||
loginService.submitSaveRequest(old.getSession(), old);
|
||||
unregistered++;
|
||||
}
|
||||
|
||||
for (Player p : world.getPlayerRepository()) {
|
||||
GameSession session = p.getSession();
|
||||
if (session != null) {
|
||||
session.handlePendingEvents(chainGroup);
|
||||
}
|
||||
}
|
||||
|
||||
world.pulse();
|
||||
|
||||
synchronizer.synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a player (may block!).
|
||||
* @param player The player.
|
||||
* @return A {@link RegistrationStatus}.
|
||||
*/
|
||||
public RegistrationStatus registerPlayer(Player player) {
|
||||
synchronized (this) {
|
||||
return World.getWorld().register(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a player. Returns immediately. The player is unregistered
|
||||
* at the start of the next cycle.
|
||||
* @param player The player.
|
||||
*/
|
||||
public void unregisterPlayer(Player player) {
|
||||
oldPlayers.add(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes the unregistration of a player.
|
||||
* @param player The player.
|
||||
*/
|
||||
public void finalizePlayerUnregistration(Player player) {
|
||||
synchronized (this) {
|
||||
World.getWorld().unregister(player);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.apollo.game.action;
|
||||
|
||||
import org.apollo.game.model.Character;
|
||||
import org.apollo.game.scheduling.ScheduledTask;
|
||||
|
||||
/**
|
||||
* An action is a specialised {@link ScheduledTask} which is specific to a
|
||||
* character.
|
||||
* <p>
|
||||
* <strong>ALL</strong> actions <strong>MUST</strong> implement the
|
||||
* {@link #equals(Object)} method. This is to check if two actions are
|
||||
* identical: if they are, then the new action does not replace the old one (so
|
||||
* spam/accidental clicking won't cancel your action, and start another from
|
||||
* scratch).
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class Action<T extends Character> extends ScheduledTask {
|
||||
|
||||
/**
|
||||
* The character performing the action.
|
||||
*/
|
||||
private final T character;
|
||||
|
||||
/**
|
||||
* A flag indicating if this action is stopping.
|
||||
*/
|
||||
private boolean stopping = false;
|
||||
|
||||
/**
|
||||
* Creates a new action.
|
||||
* @param delay The delay in pulses.
|
||||
* @param immediate A flag indicating if the action should happen
|
||||
* immediately.
|
||||
* @param character The character performing the action.
|
||||
*/
|
||||
public Action(int delay, boolean immediate, T character) {
|
||||
super(delay, immediate);
|
||||
this.character = character;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character which performed the action.
|
||||
* @return The character.
|
||||
*/
|
||||
public T getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
if (!stopping) {
|
||||
stopping = true;
|
||||
character.stopAction();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.apollo.game.action;
|
||||
|
||||
import org.apollo.game.model.Character;
|
||||
import org.apollo.game.model.Position;
|
||||
|
||||
/**
|
||||
* An @{link Action} which fires when a distance requirement is met.
|
||||
* @author Blake
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class DistancedAction<T extends Character> extends Action<T> {
|
||||
|
||||
/**
|
||||
* The position to distance check with.
|
||||
*/
|
||||
private final Position position;
|
||||
|
||||
/**
|
||||
* The minimum distance before the action fires.
|
||||
*/
|
||||
private final int distance;
|
||||
|
||||
/**
|
||||
* The delay once the threshold is reached.
|
||||
*/
|
||||
private final int delay;
|
||||
|
||||
/**
|
||||
* A flag indicating if this action fires immediately after the threshold
|
||||
* is reached.
|
||||
*/
|
||||
private final boolean immediate;
|
||||
|
||||
/**
|
||||
* A flag indicating if the distance has been reached yet.
|
||||
*/
|
||||
private boolean reached = false;
|
||||
|
||||
/**
|
||||
* Creates a new DistancedAction.
|
||||
* @param delay The delay between executions once the distance threshold is
|
||||
* reached.
|
||||
* @param immediate Whether or not this action fires immediately after the
|
||||
* distance threshold is reached.
|
||||
* @param character The character.
|
||||
* @param position The position.
|
||||
* @param distance The distance.
|
||||
*/
|
||||
public DistancedAction(int delay, boolean immediate, T character, Position position, int distance) {
|
||||
super(0, true, character);
|
||||
this.position = position;
|
||||
this.distance = distance;
|
||||
this.delay = delay;
|
||||
this.immediate = immediate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (reached) {
|
||||
// some actions (e.g. agility) will cause the player to move away again
|
||||
// so we don't check once the player got close enough once
|
||||
executeAction();
|
||||
} else if (getCharacter().getPosition().getDistance(position) <= distance) {
|
||||
reached = true;
|
||||
setDelay(delay);
|
||||
if (immediate) { // TODO: required?
|
||||
executeAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the actual action. Called when the distance requirement is met.
|
||||
*/
|
||||
public abstract void executeAction();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Contains classes related to actions, specialised scheduled tasks which
|
||||
* characters perform.
|
||||
*/
|
||||
package org.apollo.game.action;
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
/**
|
||||
* Represents a command.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class Command {
|
||||
|
||||
/**
|
||||
* The name of the command.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* The command's arguments.
|
||||
*/
|
||||
private final String[] arguments;
|
||||
|
||||
/**
|
||||
* Creates the command.
|
||||
* @param name The name of the command.
|
||||
* @param arguments The command's arguments.
|
||||
*/
|
||||
public Command(String name, String[] arguments) {
|
||||
this.name = name;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the command.
|
||||
* @return The name of the command.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command's arguments.
|
||||
* @return The command's arguments.
|
||||
*/
|
||||
public String[] getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A class which dispatches {@link Command}s to {@link CommandListener}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CommandDispatcher {
|
||||
|
||||
/**
|
||||
* A map of event listeners.
|
||||
*/
|
||||
private final Map<String, CommandListener> listeners = new HashMap<String, CommandListener>();
|
||||
|
||||
/**
|
||||
* Creates the command dispatcher and registers a listener for the credits
|
||||
* command.
|
||||
*/
|
||||
public CommandDispatcher() {
|
||||
// not in a plugin so it is harder for people to remove!
|
||||
listeners.put("credits", new CreditsCommandListener());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener with the
|
||||
* @param command The command's name.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public void register(String command, CommandListener listener) {
|
||||
listeners.put(command.toLowerCase(), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a command to the appropriate listener.
|
||||
* @param player The player.
|
||||
* @param command The command.
|
||||
*/
|
||||
public void dispatch(Player player, Command command) {
|
||||
CommandListener listener = listeners.get(command.getName().toLowerCase());
|
||||
if (listener != null) {
|
||||
listener.execute(player, command);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An interface which should be implemented by classes to listen to
|
||||
* {@link Command}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public interface CommandListener {
|
||||
|
||||
/**
|
||||
* Executes the action for this command.
|
||||
* @param player The player.
|
||||
* @param command The command.
|
||||
*/
|
||||
public void execute(Player player, Command command);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apollo.game.event.impl.SetInterfaceTextEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.World;
|
||||
import org.apollo.game.model.inter.quest.QuestConstants;
|
||||
import org.apollo.util.plugin.PluginManager;
|
||||
|
||||
/**
|
||||
* Implements a {@code ::credits} command that lists the authors of all plugins
|
||||
* used in the server.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CreditsCommandListener implements CommandListener {
|
||||
|
||||
/*
|
||||
* If you are considering removing this command, please bear in mind that
|
||||
* Apollo took several people thousands of hours to create. We released it
|
||||
* to the world for free and it isn't much to ask to leave this command in.
|
||||
* It isn't very obtrusive and gives us some well-deserved recognition for
|
||||
* the work we have done. Thank you!
|
||||
*
|
||||
* The list of authors is generated from the plugin manager. If you create
|
||||
* a custom plugin, make sure you add your name to the plugin.xml file and
|
||||
* it'll appear here automatically!
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void execute(Player player, Command command) {
|
||||
PluginManager mgr = World.getWorld().getPluginManager();
|
||||
Iterator<String> it = mgr.createAuthorsIterator();
|
||||
|
||||
int pos = 0;
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "@dre@Apollo"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "@dre@Introduction"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], ""));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "This server is based on Apollo, a lightweight, fast, secure"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "and open-source RuneScape emulator. For more"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "information about Apollo, visit the website at:"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "@dbl@https://github.com/apollo-rsps/apollo"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], ""));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "Apollo is released under the terms of the ISC"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "license, details can be found in the root folder of the "));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "Apollo distribution."));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], ""));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], "@dre@Credits"));
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos++], ""));
|
||||
|
||||
for (; pos < QuestConstants.QUEST_TEXT.length; pos++) {
|
||||
String text = it.hasNext() ? it.next() : "";
|
||||
player.send(new SetInterfaceTextEvent(QuestConstants.QUEST_TEXT[pos], text));
|
||||
}
|
||||
|
||||
player.getInterfaceSet().openWindow(QuestConstants.QUEST_INTERFACE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.apollo.game.command;
|
||||
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.Player.PrivilegeLevel;
|
||||
|
||||
/**
|
||||
* A {@link CommandListener} which checks the {@link PrivilegeLevel} of the
|
||||
* {@link Player} executing the command.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class PrivilegedCommandListener implements CommandListener {
|
||||
|
||||
/**
|
||||
* The minimum privilege level.
|
||||
*/
|
||||
private final PrivilegeLevel level;
|
||||
|
||||
/**
|
||||
* Creates the privileged command listener with the specified minimum level.
|
||||
* @param level The minimum privilege level.
|
||||
*/
|
||||
public PrivilegedCommandListener(PrivilegeLevel level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a privileged command.
|
||||
* @param player The player.
|
||||
* @param command The command.
|
||||
*/
|
||||
public abstract void executePrivileged(Player player, Command command);
|
||||
|
||||
@Override
|
||||
public final void execute(Player player, Command command) {
|
||||
if (player.getPrivilegeLevel().toInteger() >= level.toInteger()) {
|
||||
executePrivileged(player, command);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes related to in-game commands.
|
||||
*/
|
||||
package org.apollo.game.command;
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.apollo.game.event;
|
||||
|
||||
/**
|
||||
* Represents an event that can occur in the game world.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.apollo.game.event.handler;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A class which handles events.
|
||||
* @author Graham
|
||||
* @param <E> The type of event this class handles.
|
||||
*/
|
||||
public abstract class EventHandler<E extends Event> {
|
||||
|
||||
/**
|
||||
* Handles an event.
|
||||
* @param ctx The context.
|
||||
* @param player The player.
|
||||
* @param event The event.
|
||||
*/
|
||||
public abstract void handle(EventHandlerContext ctx, Player player, E event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.apollo.game.event.handler;
|
||||
|
||||
import org.apollo.game.event.handler.chain.EventHandlerChain;
|
||||
|
||||
/**
|
||||
* Provides operations specific to an {@link EventHandler} in an
|
||||
* {@link EventHandlerChain}.
|
||||
* @author Graham
|
||||
*/
|
||||
public abstract class EventHandlerContext {
|
||||
|
||||
/**
|
||||
* Breaks the handler chain.
|
||||
*/
|
||||
public abstract void breakHandlerChain();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.apollo.game.event.handler.chain;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A chain of event handlers.
|
||||
* @author Graham
|
||||
* @param <E> The type of event the handlers in this chain handle.
|
||||
*/
|
||||
public final class EventHandlerChain<E extends Event> {
|
||||
|
||||
/**
|
||||
* The handlers.
|
||||
*/
|
||||
private EventHandler<E>[] handlers;
|
||||
|
||||
/**
|
||||
* Creates the event handler chain.
|
||||
* @param handlers The handlers.
|
||||
*/
|
||||
public EventHandlerChain(EventHandler<E>... handlers) {
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically adds an event handler to the end of the chain.
|
||||
* @param handler The handler.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addLast(EventHandler<E> handler) {
|
||||
EventHandler<E>[] old = handlers;
|
||||
handlers = new EventHandler[old.length + 1];
|
||||
System.arraycopy(old, 0, handlers, 0, old.length);
|
||||
handlers[old.length] = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event, passing it down the chain until the chain is broken
|
||||
* or the event reaches the end of the chain.
|
||||
* @param player The player.
|
||||
* @param event The event.
|
||||
*/
|
||||
public void handle(Player player, E event) {
|
||||
final boolean[] running = new boolean[1];
|
||||
running[0] = true;
|
||||
|
||||
EventHandlerContext ctx = new EventHandlerContext() {
|
||||
|
||||
@Override
|
||||
public void breakHandlerChain() {
|
||||
running[0] = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
for (EventHandler<E> handler : handlers) {
|
||||
handler.handle(ctx, player, event);
|
||||
if (!running[0]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.apollo.game.event.handler.chain;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* A group of {@link EventHandlerChain}s classified by the {@link Event} type.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EventHandlerChainGroup {
|
||||
|
||||
/**
|
||||
* The map of event classes to event handler chains.
|
||||
*/
|
||||
private final Map<Class<? extends Event>, EventHandlerChain<?>> chains;
|
||||
|
||||
/**
|
||||
* Creates the event handler chain group.
|
||||
* @param chains The chains map.
|
||||
*/
|
||||
public EventHandlerChainGroup(Map<Class<? extends Event>, EventHandlerChain<?>> chains) {
|
||||
this.chains = chains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link EventHandlerChain} from this group.
|
||||
* @param <E> The type of event.
|
||||
* @param clazz The event class.
|
||||
* @return The {@link EventHandlerChain} if one was found, {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends Event> EventHandlerChain<E> getChain(Class<E> clazz) {
|
||||
return (EventHandlerChain<E>) chains.get(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes related to the chaining of event handlers.
|
||||
*/
|
||||
package org.apollo.game.event.handler.chain;
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ButtonEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} which responds to {@link ButtonEvent}s for
|
||||
* withdrawing items as notes.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class BankButtonEventHandler extends EventHandler<ButtonEvent> {
|
||||
|
||||
/**
|
||||
* The withdraw as item button id.
|
||||
*/
|
||||
private static final int WITHDRAW_AS_ITEM = 5387;
|
||||
|
||||
/**
|
||||
* The withdraw as note button id.
|
||||
*/
|
||||
private static final int WITHDRAW_AS_NOTE = 5386;
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ButtonEvent event) {
|
||||
if (event.getInterfaceId() == WITHDRAW_AS_ITEM) {
|
||||
player.setWithdrawingNotes(false);
|
||||
} else if (event.getInterfaceId() == WITHDRAW_AS_NOTE) {
|
||||
player.setWithdrawingNotes(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ItemActionEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.inter.bank.BankConstants;
|
||||
import org.apollo.game.model.inter.bank.BankDepositEnterAmountListener;
|
||||
import org.apollo.game.model.inter.bank.BankUtils;
|
||||
import org.apollo.game.model.inter.bank.BankWithdrawEnterAmountListener;
|
||||
|
||||
/**
|
||||
* An event handler which handles withdrawing and depositing items from/to a
|
||||
* player's bank.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class BankEventHandler extends EventHandler<ItemActionEvent> {
|
||||
|
||||
/**
|
||||
* Converts an option to an amount.
|
||||
* @param option The option.
|
||||
* @return The amount.
|
||||
* @throws IllegalArgumentException if the option is not legal.
|
||||
*/
|
||||
private static final int optionToAmount(int option) {
|
||||
switch (option) {
|
||||
case 1:
|
||||
return 1;
|
||||
case 2:
|
||||
return 5;
|
||||
case 3:
|
||||
return 10;
|
||||
case 4:
|
||||
return Integer.MAX_VALUE;
|
||||
case 5:
|
||||
return -1;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ItemActionEvent event) {
|
||||
if (!player.getInterfaceSet().contains(BankConstants.BANK_WINDOW_ID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getInterfaceId() == BankConstants.SIDEBAR_INVENTORY_ID) {
|
||||
deposit(ctx, player, event);
|
||||
} else if (event.getInterfaceId() == BankConstants.BANK_INVENTORY_ID) {
|
||||
withdraw(ctx, player, event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a withdraw action.
|
||||
* @param ctx The event handler context.
|
||||
* @param player The player.
|
||||
* @param event The event.
|
||||
*/
|
||||
private void withdraw(EventHandlerContext ctx, Player player, ItemActionEvent event) {
|
||||
int amount = optionToAmount(event.getOption());
|
||||
if (amount == -1) {
|
||||
player.getInterfaceSet().openEnterAmountDialog(new BankWithdrawEnterAmountListener(player, event.getSlot(), event.getId()));
|
||||
} else {
|
||||
if (!BankUtils.withdraw(player, event.getSlot(), event.getId(), amount)) {
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a deposit action.
|
||||
* @param ctx The event handler context.
|
||||
* @param player The player.
|
||||
* @param event The event.
|
||||
*/
|
||||
private void deposit(EventHandlerContext ctx, Player player, ItemActionEvent event) {
|
||||
int amount = optionToAmount(event.getOption());
|
||||
if (amount == -1) {
|
||||
player.getInterfaceSet().openEnterAmountDialog(new BankDepositEnterAmountListener(player, event.getSlot(), event.getId()));
|
||||
} else {
|
||||
if (!BankUtils.deposit(player, event.getSlot(), event.getId(), amount)) {
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.CharacterDesignEvent;
|
||||
import org.apollo.game.event.impl.CloseInterfaceEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A handler which handles {@link CharacterDesignEvent}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CharacterDesignEventHandler extends EventHandler<CharacterDesignEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, CharacterDesignEvent event) {
|
||||
player.setAppearance(event.getAppearance());
|
||||
player.setDesignedCharacter(true);
|
||||
player.send(new CloseInterfaceEvent());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.CharacterDesignEvent;
|
||||
import org.apollo.game.model.Appearance;
|
||||
import org.apollo.game.model.Gender;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* A handler which verifies {@link CharacterDesignEvent}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CharacterDesignVerificationHandler extends EventHandler<CharacterDesignEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, CharacterDesignEvent event) {
|
||||
if (!valid(event.getAppearance()) || player.hasDesignedCharacter()) {
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an appearance combination is valid.
|
||||
* @param appearance The appearance combination.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
private boolean valid(Appearance appearance) {
|
||||
int[] colors = appearance.getColors();
|
||||
int[] maxColors = new int[] { 11, 15, 15, 5, 7 };
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
if (colors[i] < 0 || colors[i] > maxColors[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Gender gender = appearance.getGender();
|
||||
if (gender == Gender.MALE) {
|
||||
return validMaleStyle(appearance);
|
||||
} else if (gender == Gender.FEMALE) {
|
||||
return validFemaleStyle(appearance);
|
||||
} else {
|
||||
return false; // maybe null?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a {@link Gender#MALE} style combination is valid.
|
||||
* @param appearance The appearance combination.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
private boolean validMaleStyle(Appearance appearance) {
|
||||
int[] styles = appearance.getStyle();
|
||||
int[] minStyles = new int[] { 0, 10, 18, 26, 33, 36, 42 };
|
||||
int[] maxStyles = new int[] { 8, 17, 25, 31, 34, 40, 43 };
|
||||
for (int i = 0; i < styles.length; i++) {
|
||||
if (styles[i] < minStyles[i] || styles[i] > maxStyles[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a {@link Gender#FEMALE} style combination is valid.
|
||||
* @param appearance The appearance combination.
|
||||
* @return {@code true} if so, {@code false} if not.
|
||||
*/
|
||||
private boolean validFemaleStyle(Appearance appearance) {
|
||||
int[] styles = appearance.getStyle();
|
||||
int[] minStyles = new int[] { 45, 255, 56, 61, 67, 70, 79 };
|
||||
int[] maxStyles = new int[] { 54, 255, 60, 65, 68, 77, 80 };
|
||||
for (int i = 0; i < styles.length; i++) {
|
||||
if (styles[i] < minStyles[i] || styles[i] > maxStyles[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ChatEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.sync.block.SynchronizationBlock;
|
||||
|
||||
/**
|
||||
* An event handler which broadcasts public chat messages.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ChatEventHandler extends EventHandler<ChatEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ChatEvent event) {
|
||||
player.getBlockSet().add(SynchronizationBlock.createChatBlock(player, event));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ChatEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An event handler which verifies chat events.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ChatVerificationHandler extends EventHandler<ChatEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ChatEvent event) {
|
||||
int color = event.getTextColor();
|
||||
int effects = event.getTextEffects();
|
||||
if (color < 0 || color > 11 || effects < 0 || effects > 5) {
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ClosedInterfaceEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} for the {@link ClosedInterfaceEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ClosedInterfaceEventHandler extends EventHandler<ClosedInterfaceEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ClosedInterfaceEvent event) {
|
||||
player.getInterfaceSet().interfaceClosed();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.command.Command;
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.CommandEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.World;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} which dispatches {@link CommandEvent}s.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CommandEventHandler extends EventHandler<CommandEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, CommandEvent event) {
|
||||
String str = event.getCommand();
|
||||
String[] components = str.split(" ");
|
||||
|
||||
String name = components[0];
|
||||
String[] arguments = new String[components.length - 1];
|
||||
|
||||
System.arraycopy(components, 1, arguments, 0, arguments.length);
|
||||
|
||||
Command command = new Command(name, arguments);
|
||||
|
||||
World.getWorld().getCommandDispatcher().dispatch(player, command);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.EnteredAmountEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} for the {@link EnteredAmountEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EnteredAmountEventHandler extends EventHandler<EnteredAmountEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, EnteredAmountEvent event) {
|
||||
player.getInterfaceSet().enteredAmount(event.getAmount());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.EquipEvent;
|
||||
import org.apollo.game.model.EquipmentConstants;
|
||||
import org.apollo.game.model.Inventory;
|
||||
import org.apollo.game.model.Item;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.Skill;
|
||||
import org.apollo.game.model.SkillSet;
|
||||
import org.apollo.game.model.def.EquipmentDefinition;
|
||||
import org.apollo.game.model.def.ItemDefinition;
|
||||
import org.apollo.game.model.inv.SynchronizationInventoryListener;
|
||||
|
||||
/**
|
||||
* An event handler which equips items.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EquipEventHandler extends EventHandler<EquipEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, EquipEvent event) {
|
||||
if (event.getInterfaceId() == SynchronizationInventoryListener.INVENTORY_ID) {
|
||||
int slot = event.getSlot();
|
||||
if (slot < 0 || slot >= player.getInventory().capacity()) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = player.getInventory().get(slot);
|
||||
if (item == null || item.getId() != event.getId()) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
ItemDefinition itemDef = item.getDefinition();
|
||||
EquipmentDefinition equipDef = EquipmentDefinition.forId(item.getId());
|
||||
if (equipDef == null) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
SkillSet skillSet = player.getSkillSet();
|
||||
if (skillSet.getSkill(Skill.ATTACK).getMaximumLevel() < equipDef.getAttackLevel()) {
|
||||
player.sendMessage("You need an Attack level of " + equipDef.getAttackLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
if (skillSet.getSkill(Skill.STRENGTH).getMaximumLevel() < equipDef.getStrengthLevel()) {
|
||||
player.sendMessage("You need a Strength level of " + equipDef.getStrengthLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
if (skillSet.getSkill(Skill.DEFENCE).getMaximumLevel() < equipDef.getDefenceLevel()) {
|
||||
player.sendMessage("You need a Defence level of " + equipDef.getDefenceLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
if (skillSet.getSkill(Skill.RANGED).getMaximumLevel() < equipDef.getRangedLevel()) {
|
||||
player.sendMessage("You need a Ranged level of " + equipDef.getRangedLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
if (skillSet.getSkill(Skill.MAGIC).getMaximumLevel() < equipDef.getMagicLevel()) {
|
||||
player.sendMessage("You need a Magic level of " + equipDef.getMagicLevel() + " to equip this item.");
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory inventory = player.getInventory();
|
||||
Inventory equipment = player.getEquipment();
|
||||
|
||||
int equipmentSlot = equipDef.getSlot();
|
||||
|
||||
// TODO: equip event decoder for 317, and remove event decoder for both
|
||||
// TODO: put all this into another method somewhere
|
||||
|
||||
// check if there is enough space for a two handed weapon
|
||||
if (equipDef.isTwoHanded()) {
|
||||
Item currentShield = equipment.get(EquipmentConstants.SHIELD);
|
||||
if (currentShield != null) {
|
||||
if (inventory.freeSlots() < 1) {
|
||||
inventory.forceCapacityExceeded();
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if a shield is being added with a two handed weapon
|
||||
boolean removeWeapon = false;
|
||||
if (equipmentSlot == EquipmentConstants.SHIELD) {
|
||||
Item currentWeapon = equipment.get(EquipmentConstants.WEAPON);
|
||||
if (currentWeapon != null) {
|
||||
EquipmentDefinition weaponDef = EquipmentDefinition.forId(currentWeapon.getId());
|
||||
if (weaponDef.isTwoHanded()) {
|
||||
if (inventory.freeSlots() < 1) {
|
||||
inventory.forceCapacityExceeded();
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
removeWeapon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item previous = equipment.get(equipmentSlot);
|
||||
if (itemDef.isStackable() && previous != null && previous.getId() == item.getId()) {
|
||||
// we know the item is there, so we can let the inventory class do its stacking magic
|
||||
inventory.remove(item);
|
||||
Item tmp = equipment.add(item);
|
||||
if (tmp != null) {
|
||||
inventory.add(tmp);
|
||||
}
|
||||
} else {
|
||||
// swap the weapons around
|
||||
Item tmp = equipment.reset(equipmentSlot);
|
||||
equipment.set(equipmentSlot, item);
|
||||
inventory.reset(slot);
|
||||
if (tmp != null) {
|
||||
inventory.add(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
// remove the shield if this weapon is two handed
|
||||
if (equipDef.isTwoHanded()) {
|
||||
Item tmp = equipment.reset(EquipmentConstants.SHIELD);
|
||||
// we know tmp will not be null from the check above
|
||||
inventory.add(tmp);
|
||||
}
|
||||
|
||||
if (removeWeapon) {
|
||||
Item tmp = equipment.reset(EquipmentConstants.WEAPON);
|
||||
// we know tmp will not be null from the check about
|
||||
inventory.add(tmp);
|
||||
}
|
||||
|
||||
ctx.breakHandlerChain();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.ItemActionEvent;
|
||||
import org.apollo.game.model.Inventory;
|
||||
import org.apollo.game.model.Item;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.inv.SynchronizationInventoryListener;
|
||||
|
||||
/**
|
||||
* An event handler which removes equipped items.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class RemoveEventHandler extends EventHandler<ItemActionEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, ItemActionEvent event) {
|
||||
if (event.getOption() == 1 && event.getInterfaceId() == SynchronizationInventoryListener.EQUIPMENT_ID) {
|
||||
Inventory inventory = player.getInventory();
|
||||
Inventory equipment = player.getEquipment();
|
||||
|
||||
int slot = event.getSlot();
|
||||
if (slot < 0 || slot >= equipment.capacity()) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = equipment.get(slot);
|
||||
if (item == null || item.getId() != event.getId()) {
|
||||
ctx.breakHandlerChain();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean removed = true;
|
||||
|
||||
inventory.stopFiringEvents();
|
||||
equipment.stopFiringEvents();
|
||||
|
||||
try {
|
||||
equipment.set(slot, null);
|
||||
Item tmp = inventory.add(item);
|
||||
if (tmp != null) {
|
||||
removed = false;
|
||||
equipment.set(slot, tmp);
|
||||
}
|
||||
} finally {
|
||||
inventory.startFiringEvents();
|
||||
equipment.startFiringEvents();
|
||||
}
|
||||
|
||||
if (removed) {
|
||||
inventory.forceRefresh(); // TODO find out the specific slot that got used?
|
||||
equipment.forceRefresh(slot);
|
||||
} else {
|
||||
inventory.forceCapacityExceeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.SwitchItemEvent;
|
||||
import org.apollo.game.model.Inventory;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.inter.bank.BankConstants;
|
||||
import org.apollo.game.model.inv.SynchronizationInventoryListener;
|
||||
|
||||
/**
|
||||
* An {@link EventHandler} which updates an {@link Inventory} when the client
|
||||
* sends a {@link SwitchItemEvent} to the server.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class SwitchItemEventHandler extends EventHandler<SwitchItemEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, SwitchItemEvent event) {
|
||||
Inventory inventory;
|
||||
boolean insertPermitted = false;
|
||||
|
||||
// TODO is there a better way of doing this??
|
||||
switch (event.getInterfaceId()) {
|
||||
case SynchronizationInventoryListener.INVENTORY_ID:
|
||||
case BankConstants.SIDEBAR_INVENTORY_ID:
|
||||
inventory = player.getInventory();
|
||||
break;
|
||||
case SynchronizationInventoryListener.EQUIPMENT_ID:
|
||||
inventory = player.getEquipment();
|
||||
break;
|
||||
case BankConstants.BANK_INVENTORY_ID:
|
||||
inventory = player.getBank();
|
||||
insertPermitted = true;
|
||||
break;
|
||||
default:
|
||||
return; // not a known inventory, ignore
|
||||
}
|
||||
|
||||
if (event.getOldSlot() >= 0 && event.getNewSlot() >= 0 && event.getOldSlot() < inventory.capacity() && event.getNewSlot() < inventory.capacity()) {
|
||||
// events must be fired for it to work if a sidebar inv overlay is used
|
||||
inventory.swap(insertPermitted ? event.isInserting() : false, event.getOldSlot(), event.getNewSlot());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.apollo.game.event.handler.impl;
|
||||
|
||||
import org.apollo.game.event.handler.EventHandler;
|
||||
import org.apollo.game.event.handler.EventHandlerContext;
|
||||
import org.apollo.game.event.impl.WalkEvent;
|
||||
import org.apollo.game.model.Player;
|
||||
import org.apollo.game.model.Position;
|
||||
import org.apollo.game.model.WalkingQueue;
|
||||
|
||||
/**
|
||||
* A handler for the {@link WalkEvent}.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class WalkEventHandler extends EventHandler<WalkEvent> {
|
||||
|
||||
@Override
|
||||
public void handle(EventHandlerContext ctx, Player player, WalkEvent event) {
|
||||
WalkingQueue queue = player.getWalkingQueue();
|
||||
|
||||
Position[] steps = event.getSteps();
|
||||
for (int i = 0; i < steps.length; i++) {
|
||||
Position step = steps[i];
|
||||
if (i == 0) {
|
||||
if (!queue.addFirstStep(step)) {
|
||||
return; /* ignore packet */
|
||||
}
|
||||
} else {
|
||||
queue.addStep(step);
|
||||
}
|
||||
}
|
||||
|
||||
queue.setRunningQueue(event.isRunning());
|
||||
|
||||
if (queue.size() > 0) {
|
||||
player.stopAction();
|
||||
player.getInterfaceSet().close(); // TODO: should this be done if size == 0?
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains event handler implementations.
|
||||
*/
|
||||
package org.apollo.game.event.handler.impl;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes which define abstract event handlers.
|
||||
*/
|
||||
package org.apollo.game.event.handler;
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent when the client clicks a button.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ButtonEvent extends Event {
|
||||
|
||||
/**
|
||||
* The interface id.
|
||||
*/
|
||||
private final int interfaceId;
|
||||
|
||||
/**
|
||||
* Creates the button event.
|
||||
* @param interfaceId The interface id.
|
||||
*/
|
||||
public ButtonEvent(int interfaceId) {
|
||||
this.interfaceId = interfaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interface id.
|
||||
* @return The interface id.
|
||||
*/
|
||||
public int getInterfaceId() {
|
||||
return interfaceId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
import org.apollo.game.model.Appearance;
|
||||
|
||||
/**
|
||||
* An event sent by the client when the player modifies their character's
|
||||
* design.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CharacterDesignEvent extends Event {
|
||||
|
||||
/**
|
||||
* The appearance.
|
||||
*/
|
||||
private final Appearance appearance;
|
||||
|
||||
/**
|
||||
* Creates the character design event.
|
||||
* @param appearance The appearance.
|
||||
*/
|
||||
public CharacterDesignEvent(Appearance appearance) {
|
||||
this.appearance = appearance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appearance.
|
||||
* @return The appearance.
|
||||
*/
|
||||
public Appearance getAppearance() {
|
||||
return appearance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event sent by the client to send a public chat message to other players.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ChatEvent extends Event {
|
||||
|
||||
/**
|
||||
* The message.
|
||||
*/
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* The compressed message.
|
||||
*/
|
||||
private final byte[] compressedMessage;
|
||||
|
||||
/**
|
||||
* The text color.
|
||||
*/
|
||||
private final int color;
|
||||
|
||||
/**
|
||||
* The text effects.
|
||||
*/
|
||||
private final int effects;
|
||||
|
||||
/**
|
||||
* Creates a new chat event.
|
||||
* @param message The message.
|
||||
* @param compressedMessage The compressed message.
|
||||
* @param color The text color.
|
||||
* @param effects The text effects.
|
||||
*/
|
||||
public ChatEvent(String message, byte[] compressedMessage, int color, int effects) {
|
||||
this.message = message;
|
||||
this.compressedMessage = compressedMessage;
|
||||
this.color = color;
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message.
|
||||
* @return The message.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text color.
|
||||
* @return The text color.
|
||||
*/
|
||||
public int getTextColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text effects.
|
||||
* @return The text effects.
|
||||
*/
|
||||
public int getTextEffects() {
|
||||
return effects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compressed message.
|
||||
* @return The compressed message.
|
||||
*/
|
||||
public byte[] getCompressedMessage() {
|
||||
return compressedMessage;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event which closes the open interface.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CloseInterfaceEvent extends Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* Sent by the client when the current interface is closed.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ClosedInterfaceEvent extends Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An event issued by the client to send a {@code ::} command/
|
||||
* @author Graham
|
||||
*/
|
||||
public final class CommandEvent extends Event {
|
||||
|
||||
/**
|
||||
* The command.
|
||||
*/
|
||||
private final String command;
|
||||
|
||||
/**
|
||||
* Creates the command event.
|
||||
* @param command The command.
|
||||
*/
|
||||
public CommandEvent(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command.
|
||||
* @return The command.
|
||||
*/
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.apollo.game.event.impl;
|
||||
|
||||
import org.apollo.game.event.Event;
|
||||
|
||||
/**
|
||||
* An {@link Event} which is sent to the client to open up the enter amount
|
||||
* interface.
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EnterAmountEvent extends Event {
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user