diff --git a/.gitignore b/.gitignore
index 07a10d66..e3e103fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
/data/fs
/data/savedGames
/bin/
+/lib/
\ No newline at end of file
diff --git a/data/note-317.dat b/data/note-317.dat
deleted file mode 100644
index 8438dbb2..00000000
Binary files a/data/note-317.dat and /dev/null differ
diff --git a/data/note-377.dat b/data/note-377.dat
deleted file mode 100644
index 488fec5b..00000000
Binary files a/data/note-377.dat and /dev/null differ
diff --git a/data/plugins/areas/actions.rb b/data/plugins/areas/actions.rb
index e72f42f4..58eab91e 100644
--- a/data/plugins/areas/actions.rb
+++ b/data/plugins/areas/actions.rb
@@ -3,8 +3,20 @@ require 'java'
java_import 'org.apollo.game.message.impl.DisplayCrossbonesMessage'
java_import 'org.apollo.game.model.entity.Player'
+
+
+# Registers an area action.
+def area_action(name, &block)
+ AREA_ACTIONS[name] = action = AreaAction.new
+ action.instance_eval(&block)
+end
+
+
AREA_ACTIONS = {}
+
+private
+
# An action that is called when a player enters or exits an area.
class AreaAction
@@ -38,31 +50,4 @@ class AreaAction
@on_exit.call(player) unless @on_exit.nil?
end
-end
-
-# Registers an area action.
-def area_action(name, &block)
- AREA_ACTIONS[name] = action = AreaAction.new
- action.instance_eval(&block)
-end
-
-# Defines the pvp area action.
-area_action :pvp do
- on_entry { |player| player.in_pvp = true }
- on_exit { |player| player.in_pvp = false }
-end
-
-# Defines the wilderness area action.
-area_action :wilderness do
-
- on_entry do |player|
- player.send(DisplayCrossbonesMessage.new(true))
- player.in_wilderness = true
- end
-
- on_exit do |player|
- player.send(DisplayCrossbonesMessage.new(false))
- player.in_wilderness = false
- end
-
end
\ No newline at end of file
diff --git a/data/plugins/areas/areas.rb b/data/plugins/areas/areas.rb
index cb540224..369dd2b7 100644
--- a/data/plugins/areas/areas.rb
+++ b/data/plugins/areas/areas.rb
@@ -1,11 +1,26 @@
require 'java'
+java_import 'org.apollo.game.model.Position'
+java_import 'org.apollo.game.model.entity.Entity$EntityType'
java_import 'org.apollo.game.model.entity.Player'
-# Todo make 0 the default height
+
+
+# Creates a new area and registers it with the supplied coordinates.
+def area(hash)
+ raise 'Hash must contain a name, coordinates, and actions pair.' unless hash.has_keys?(:name, :coordinates, :actions)
+ name = hash[:name]; coordinates = hash[:coordinates]; actions = hash[:actions]
+
+ actions = [ actions ] if actions.is_a?(Symbol)
+ actions.map! { |action| AREA_ACTIONS[action]}
+ @areas << Area.new(name, coordinates, actions)
+end
+
+
+private
# A map of coordinates (as an array) to areas.
-AREAS = []
+@areas = []
# An area of the game world.
class Area
@@ -16,31 +31,70 @@ class Area
@actions = actions
end
- # Called when the player has entered the area.
- def entered(player)
- actions.each { |action| action.entered(player) }
+ def min_x() # TODO better data structure and methods than this
+ @coordinates[0]
end
- # Called whilst the player is inside the area.
+ def min_y()
+ @coordinates[1]
+ end
+
+ def max_x()
+ @coordinates[2]
+ end
+
+ def max_y()
+ @coordinates[3]
+ end
+
+ def height()
+ @coordinates[4]
+ end
+
+ # Called when the player has entered the area.
+ def entered(player)
+ @actions.each { |action| action.entered(player) }
+ end
+
+ # Called when the player has moved, but is still inside the area (and was in the area before).
def inside(player)
- acttions.each { |action| action.inside(player) }
+ @actions.each { |action| action.inside(player) }
end
# Called when the player has exited the area.
def exited(player)
- actions.each { |action| action.exited(player) }
+ @actions.each { |action| action.exited(player) }
end
end
-# Creates a new area and registers it with the supplied coordinates.
-def area(hash)
- raise 'Hash must contain a name, coordinates, and actions pair.' unless hash.has_keys?(:name, :coordinates, :actions)
- name = hash[:name]; coordinates = hash[:coordinates]; actions = hash[:actions]
+# Listen for the MobPositionUpdateEvent and update the area listeners if appropriate.
+on :mob_position_update do |event|
+ mob = event.mob
+ next unless mob.entity_type == EntityType::PLAYER
- AREAS << Area.new(name, coordinates, actions.is_a?(Symbol) ? [actions] : actions)
+ old = mob.position
+ @areas.each do |area|
+ was_inside = old.inside(area)
+ next_inside = event.next.inside(area)
+
+ if was_inside
+ if next_inside then area.inside(mob) else area.exited(mob) end
+ else
+ area.entered(mob) if next_inside
+ end
+ end
end
-# Coordinates refer to the bottom-left position (min_x, min_y) and the top-right position (max_x, max_y), followed by the height (optional).
-area :name => :wilderness, :coordinates => [ 2944, 3520, 3392, 6400, 0 ], :actions => [ :pvp, :multicombat, :wilderness ]
-area :name => :duel_arena, :coordinates => [ 3327, 3200, 3392, 3286 ], :actions => :pvp
\ No newline at end of file
+# The existing Position class.
+class Position
+
+ # Returns whether or not this Position is inside the specified Area.
+ def inside(area)
+ return false if (x < area.min_x() || x > area.max_x() || y < area.min_y() || y > area.max_y())
+ z = area.height()
+
+ return true if (z.nil? || z == height)
+ end
+
+end
\ No newline at end of file
diff --git a/data/plugins/bootstrap.rb b/data/plugins/bootstrap.rb
index 41224dbc..134e006b 100644
--- a/data/plugins/bootstrap.rb
+++ b/data/plugins/bootstrap.rb
@@ -16,11 +16,11 @@ require 'java'
java_import 'org.apollo.game.command.CommandListener'
java_import 'org.apollo.game.message.handler.MessageHandler'
-java_import 'org.apollo.game.login.LoginListener'
-java_import 'org.apollo.game.login.LogoutListener'
java_import 'org.apollo.game.model.World'
java_import 'org.apollo.game.model.entity.Player'
-java_import 'org.apollo.game.model.setting.PrivilegeLevel'
+java_import 'org.apollo.game.model.event.EventListener'
+java_import 'org.apollo.game.model.event.PlayerEvent'
+java_import 'org.apollo.game.model.entity.setting.PrivilegeLevel'
java_import 'org.apollo.game.scheduling.ScheduledTask'
java_import 'org.apollo.util.plugin.PluginContext'
@@ -32,64 +32,61 @@ RIGHTS_STANDARD = 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
+
+ # Converts a ruby snake_case string to camel-case.
+ def camelize()
gsub(/(?:^|_)(.)/) { $1.upcase }
end
+
end
# A CommandListener that executes a Proc object with two arguments: the player and the command.
class ProcCommandListener < CommandListener
+ # Creates the ProcCommandListener.
def initialize(rights, block)
super(rights)
@block = block
end
+ # Executes the block listening for the command.
def execute(player, command)
@block.call(player, command)
end
end
-# A LoginListener that executes a Proc object with the player argument.
-class ProcLoginListener
- java_implements LoginListener
-
- def initialize(block)
- super()
- @block = block
- end
-
- def execute(player)
- @block.call(player)
- end
-end
-
# A LogoutListener that executes a Proc object with the player argument.
-class ProcLogoutListener
- java_implements LogoutListener
+class ProcEventListener
+ java_implements EventListener
+ # Creates the ProcEventListener.
def initialize(block)
super()
@block = block
end
- def execute(player)
- @block.call(player)
+ # Executes the block handling the Event.
+ def handle(event)
+ args = [ event ]
+ args << event.player if event.kind_of?(PlayerEvent)
+ @block.call(*args)
end
end
-# An MessageHandler which executes a Proc object with three arguments: the chain
+# A MessageHandler which executes a Proc object with three arguments: the chain
# context, the player and the message.
class ProcMessageHandler < MessageHandler
+ # Creates the ProcMessageListener.
def initialize(block, option)
- super() # required (with brackets!), see http://jira.codehaus.org/browse/JRUBY-679
+ super()
@block = block
@option = option
end
+ # Handles the message.
def handle(ctx, player, message)
@block.call(ctx, player, message) if (@option == 0 || @option == message.option)
end
@@ -99,11 +96,13 @@ end
# A ScheduledTask which executes a Proc object with one argument (itself).
class ProcScheduledTask < ScheduledTask
+ # Creates the ProcScheduledTask.
def initialize(delay, immediate, block)
super(delay, immediate)
@block = block
end
+ # Executes the block.
def execute
@block.call(self)
end
@@ -138,8 +137,7 @@ end
# * :command
# * :message
# * :button
-# * :login
-# * :logout
+# * Any valid Event, as a symbol in ruby snake_case form.
#
# 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
@@ -155,9 +153,10 @@ def on(type, *args, &block)
when :command then on_command(args, block)
when :message then on_message(args, block)
when :button then on_button(args, block)
- when :login then on_login(block)
- when :logout then on_logout(block)
- else raise 'Unknown message type.'
+ else
+ class_name = type.to_s.camelize.concat('Event')
+ type = Java::JavaClass.for_name("org.apollo.game.model.event.impl.#{class_name}")
+ $world.listen_for(type, ProcEventListener.new(block))
end
end
@@ -189,10 +188,9 @@ def on_message(args, proc)
end
end
- if message.is_a?(Symbol)
- class_name = message.to_s.camelize.concat('Message')
- message = Java::JavaClass.for_name("org.apollo.game.message.impl.#{class_name}")
- end
+
+ class_name = message.to_s.camelize.concat('Message')
+ message = Java::JavaClass.for_name("org.apollo.game.message.impl.#{class_name}")
$ctx.add_last_message_handler(message, ProcMessageHandler.new(proc, option))
end
diff --git a/data/plugins/chat/privacy/privacy.rb b/data/plugins/chat/privacy/privacy.rb
index 4de03b8b..8200fce7 100644
--- a/data/plugins/chat/privacy/privacy.rb
+++ b/data/plugins/chat/privacy/privacy.rb
@@ -1,6 +1,6 @@
require 'java'
-java_import 'org.apollo.game.model.setting.PrivacyState'
+java_import 'org.apollo.game.model.entity.setting.PrivacyState'
java_import 'org.apollo.game.message.impl.SendFriendMessage'
on :message, :privacy_option do |ctx, player, message|
diff --git a/data/plugins/chat/private-messaging/friend.rb b/data/plugins/chat/private-messaging/friend.rb
index dea9d769..9433659a 100644
--- a/data/plugins/chat/private-messaging/friend.rb
+++ b/data/plugins/chat/private-messaging/friend.rb
@@ -4,8 +4,8 @@ java_import 'org.apollo.game.message.impl.FriendServerStatusMessage'
java_import 'org.apollo.game.message.impl.IgnoreListMessage'
java_import 'org.apollo.game.message.impl.SendFriendMessage'
java_import 'org.apollo.game.model.World'
-java_import 'org.apollo.game.model.setting.ServerStatus'
-java_import 'org.apollo.game.model.setting.PrivacyState'
+java_import 'org.apollo.game.model.entity.setting.ServerStatus'
+java_import 'org.apollo.game.model.entity.setting.PrivacyState'
java_import 'org.apollo.game.model.entity.Player'
@@ -42,7 +42,7 @@ on :message, :remove_friend do |ctx, player, message|
end
# Update the friend server status and send the friend/ignore lists of the player logging in.
-on :login do |player|
+on :login do |event, player|
player.send(FriendServerStatusMessage.new(ServerStatus::CONNECTING))
player.send(IgnoreListMessage.new(player.ignored_usernames)) if player.ignored_usernames.size > 0
@@ -62,7 +62,7 @@ on :login do |player|
end
# Notifies the player's friends that the player has logged out.
-on :logout do |player|
+on :logout do |event, player|
update_friends(player, 0)
end
diff --git a/data/plugins/chat/private-messaging/messaging.rb b/data/plugins/chat/private-messaging/messaging.rb
index 1d91b94d..17aa7d77 100644
--- a/data/plugins/chat/private-messaging/messaging.rb
+++ b/data/plugins/chat/private-messaging/messaging.rb
@@ -2,7 +2,7 @@ require 'java'
java_import 'org.apollo.game.message.impl.ForwardPrivateChatMessage'
java_import 'org.apollo.game.model.World'
-java_import 'org.apollo.game.model.setting.PrivacyState'
+java_import 'org.apollo.game.model.entity.setting.PrivacyState'
on :message, :private_chat do |ctx, player, message|
friend = $world.get_player(message.username)
@@ -11,9 +11,9 @@ end
# Checks if the sender is permitted to interact with the friend they have added:
def interaction_permitted(sender, friend)
- if friend == nil || friend.has_ignored(sender.username)
+ if friend.nil? || friend.has_ignored(sender.username)
return false
end
- return friend.friends_with(sender.username) ? friend.friend_privacy != PrivacyState::OFF : friend.friend_privacy == PrivacyState::ON
+ return friend.friends_with(sender.username) ? (friend.friend_privacy != PrivacyState::OFF) : (friend.friend_privacy == PrivacyState::ON)
end
\ No newline at end of file
diff --git a/data/plugins/cmd/bank/bank.rb b/data/plugins/cmd/bank/bank.rb
index 6c28fa91..3b210064 100644
--- a/data/plugins/cmd/bank/bank.rb
+++ b/data/plugins/cmd/bank/bank.rb
@@ -1,8 +1,6 @@
require 'java'
-java_import 'org.apollo.game.model.inter.bank.BankUtils'
-
# Opens the player's bank.
on :command, :bank, RIGHTS_ADMIN do |player, command|
- BankUtils.open_bank(player)
+ player.open_bank
end
\ No newline at end of file
diff --git a/data/plugins/consumables/food.rb b/data/plugins/consumables/food.rb
index 025b0ea2..cfbe600b 100644
--- a/data/plugins/consumables/food.rb
+++ b/data/plugins/consumables/food.rb
@@ -4,57 +4,91 @@ java_import 'org.apollo.game.model.Animation'
java_import 'org.apollo.game.model.entity.Skill'
java_import 'org.apollo.game.model.entity.Player'
-EAT_FOOD_SOUND = 317
+private
-# Todo support other foods (e.g. pizza)
+EAT_FOOD_SOUND = 317
# Represents an edible piece of food, such as bread or fish.
class Food < Consumable
- def initialize(name, id, restoration)
+ def initialize(name, id, restoration, replace)
super(name, id, EAT_FOOD_SOUND)
@restoration = restoration
+ @replace = replace
end
# Restore the appropriate amount of hitpoints when consumed.
def consume(player)
- hitpoints = player.skill_set.skill(HITPOINTS_SKILL_ID)
+ hitpoints = player.skill_set.skill(Skill::HITPOINTS)
new_curr = [ hitpoints.current_level + @restoration, hitpoints.maximum_level ].min
- player.skill_set.set_skill(HITPOINTS_SKILL_ID, Skill.new(hitpoints.experience, new_curr, hitpoints.maximum_level))
+ player.inventory.add(@replace) unless (@replace == -1)
+
player.send_message("You eat the #{name}.", true)
+ player.send_message("It heals some health.", true) if new_curr == hitpoints
+
+ player.skill_set.set_skill(Skill::HITPOINTS, Skill.new(hitpoints.experience, new_curr, hitpoints.maximum_level))
end
end
# Appends a food item to the list of consumables.
-def append_food(hash)
+def food(hash)
raise 'Hash must contain a name, id, and a restoration value.' unless (hash.has_keys?(:name, :id, :restoration))
- name = hash[:name]; id = hash[:id]; restoration = hash[:restoration]
+ name = hash[:name]; id = hash[:id]; restoration = hash[:restoration]; replace = hash[:replace] || -1; @delay = hash[:delay] || 3
- append_consumable(Food.new(name, id, restoration))
+ append_consumable(Food.new(name, id, restoration, replace))
end
-append_food :name => :bread, :id => 2309, :restoration => 4
-append_food :name => :cooked_meat, :id => 2142, :restoration => 3
-append_food :name => :cooked_chicken, :id => 2140, :restoration => 3
-append_food :name => :ugthanki_meat, :id => 1861, :restoration => 3
+food :name => :bread, :id => 2309, :restoration => 4
+food :name => :cooked_meat, :id => 2142, :restoration => 3
+food :name => :cooked_chicken, :id => 2140, :restoration => 3
+food :name => :ugthanki_meat, :id => 1861, :restoration => 3
-append_food :name => :anchovies, :id => 319, :restoration => 1
-append_food :name => :shrimps, :id => 315, :restoration => 3
-append_food :name => :sardine, :id => 325, :restoration => 3
-append_food :name => :herring, :id => 347, :restoration => 5
-append_food :name => :mackeral, :id => 355, :restoration => 5
-append_food :name => :trout, :id => 333, :restoration => 7
-append_food :name => :cod, :id => 339, :restoration => 7
-append_food :name => :pike, :id => 351, :restoration => 8
-append_food :name => :salmon, :id => 329, :restoration => 9
-append_food :name => :tuna, :id => 361, :restoration => 10
-append_food :name => :lobster, :id => 379, :restoration => 12
-append_food :name => :bass, :id => 365, :restoration => 13
-append_food :name => :swordfish, :id => 373, :restoration => 14
-append_food :name => :monkfish, :id => 7946, :restoration => 16
-append_food :name => :shark, :id => 385, :restoration => 20
-append_food :name => :sea_turtle, :id => 397, :restoration => 21
-append_food :name => :manta_ray, :id => 391, :restoration => 22
\ No newline at end of file
+food :name => :anchovies, :id => 319, :restoration => 1
+food :name => :shrimps, :id => 315, :restoration => 3
+food :name => :sardine, :id => 325, :restoration => 3
+food :name => :herring, :id => 347, :restoration => 5
+food :name => :mackeral, :id => 355, :restoration => 5
+food :name => :trout, :id => 333, :restoration => 7
+food :name => :cod, :id => 339, :restoration => 7
+food :name => :pike, :id => 351, :restoration => 8
+food :name => :salmon, :id => 329, :restoration => 9
+food :name => :tuna, :id => 361, :restoration => 10
+food :name => :lobster, :id => 379, :restoration => 12
+food :name => :bass, :id => 365, :restoration => 13
+food :name => :swordfish, :id => 373, :restoration => 14
+food :name => :monkfish, :id => 7946, :restoration => 16
+food :name => :shark, :id => 385, :restoration => 20
+food :name => :sea_turtle, :id => 397, :restoration => 21
+food :name => :manta_ray, :id => 391, :restoration => 22
+
+food :name => :cake, :id => 1891, :restoration => 4, :replace => 1893
+food :name => :cake, :id => 1893, :restoration => 4, :replace => 1895
+food :name => :cake, :id => 1895, :restoration => 4
+
+food :name => :chocolate_cake, :id => 1897, :restoration => 5, :replace => 1899
+food :name => :chocolate_cake, :id => 1899, :restoration => 5, :replace => 1901
+food :name => :chocolate_cake, :id => 1901, :restoration => 5
+
+food :name => :plain_pizza, :id => 2289, :restoration => 7, :replace => 2291
+food :name => :plain_pizza, :id => 2291, :restoration => 7
+
+food :name => :meat_pizza, :id => 2293, :restoration => 8, :replace => 2295
+food :name => :meat_pizza, :id => 2295, :restoration => 8
+
+food :name => :anchovy_pizza, :id => 2297, :restoration => 9, :replace => 2299
+food :name => :anchovy_pizza, :id => 2299, :restoration => 9
+
+food :name => :pineapple_pizza, :id => 2301, :restoration => 11, :replace => 2303
+food :name => :pineapple_pizza, :id => 2303, :restoration => 11
+
+food :name => :redberry_pie, :id => 2325, :restoration => 5, :replace => 2333, :delay => 1
+food :name => :redberry_pie, :id => 2333, :restoration => 5, :delay => 1
+
+food :name => :meat_pie, :id => 2327, :restoration => 6, :replace => 2331, :delay => 1
+food :name => :meat_pie, :id => 2331, :restoration => 6, :delay => 1
+
+food :name => :apple_pie, :id => 2323, :restoration => 7, :replace => 2335, :delay => 1
+food :name => :apple_pie, :id => 2335, :restoration => 7, :delay => 1
diff --git a/data/plugins/consumables/plugin.xml b/data/plugins/consumables/plugin.xml
index 1017d547..50f2ea90 100644
--- a/data/plugins/consumables/plugin.xml
+++ b/data/plugins/consumables/plugin.xml
@@ -6,13 +6,12 @@
Adds support for consumables (e.g. potions, food).Major
+ Ryley
-
- skill-herblore
-
+
\ No newline at end of file
diff --git a/data/plugins/consumables/potions.rb b/data/plugins/consumables/potions.rb
index ed27a5ce..71505929 100644
--- a/data/plugins/consumables/potions.rb
+++ b/data/plugins/consumables/potions.rb
@@ -2,13 +2,23 @@ require 'java'
java_import 'org.apollo.game.model.entity.Skill'
-DRINK_POTION_SOUND = 334
+private
+
+module Constants
+
+ # The sound made when drinking a potion.
+ DRINK_POTION_SOUND = 334
+
+ # The id of an empty vial.
+ EMPTY_VIAL_ID = 229
+
+end
# A drinkable potion.
class Potion < Consumable
def initialize(id, name, doses)
- super(name, id, DRINK_POTION_SOUND)
+ super(name, id, Constants::DRINK_POTION_SOUND)
@doses = doses
end
@@ -21,7 +31,7 @@ class Potion < Consumable
player.send_message("You have #{ @doses.length - index } dose#{ "s" unless index == 3 } of potion left.", true)
else
player.send_message('You drink the last of your potion.', true)
- player.inventory.add(EMPTY_VIAL_ID)
+ player.inventory.add(Constants::EMPTY_VIAL_ID)
end
drink(player)
diff --git a/data/plugins/dialogue/dialogue.rb b/data/plugins/dialogue/dialogue.rb
index 301cf771..ffe51c9a 100644
--- a/data/plugins/dialogue/dialogue.rb
+++ b/data/plugins/dialogue/dialogue.rb
@@ -7,26 +7,65 @@ java_import 'org.apollo.game.message.impl.SetWidgetNpcModelMessage'
java_import 'org.apollo.game.message.impl.SetWidgetPlayerModelMessage'
java_import 'org.apollo.game.message.impl.SetWidgetTextMessage'
-# Defines a dialogue, with the specified name and block.
-def dialogue(name, &block)
- raise 'Dialogues must have a name and block.' if (name.nil? || block.nil?)
+# The map of conversation names to Conversations.
+CONVERSATIONS = {}
- dialogue = Dialogue.new(name)
- dialogue.instance_eval(&block)
- dialogue.wrap
- DIALOGUES[name] = dialogue
+
+# Declares a conversation.
+def conversation(name, &block)
+ conversation = Conversation.new(name)
+ conversation.instance_eval(&block)
+
+ raise "Conversation named #{name} already exists." if CONVERSATIONS.has_key?(name)
+ CONVERSATIONS[name] = conversation
end
-# Defines an opening (i.e. conversation starter) dialogue, which hooks into the chain.
-# Allows for a lambda prerequisite to be passed, which takes one argument the player; if the prerequisite evaluates to false, the dialogue will not be opened.
-def opening_dialogue(name, prerequisite=nil, &block)
- dialogue = dialogue(name, &block)
- npc = dialogue.npc
- raise 'Npc cannot be null when opening a dialogue.' if npc.nil?
+# A conversation held between two entities.
+class Conversation
- on :message, :first_npc_action, npc do |ctx, player, event|
- player.open_dialogue(name) if (prerequisite.nil? || prerequisite.call(player))
+ # Creates the Conversation.
+ def initialize(name)
+ @dialogues = {}
+ @starters = []
+ @name = name
end
+
+ # Defines a dialogue, with the specified name and block.
+ def dialogue(name, &block)
+ raise 'Dialogues must have a name and block.' if (name.nil? || block.nil?)
+
+ dialogue = Dialogue.new(name, self)
+ dialogue.instance_eval(&block)
+ dialogue.wrap
+
+ raise "Conversations #{@name} already has a dialogue named #{name}." if @dialogues.has_key?(name)
+ @dialogues[name] = dialogue
+
+ if ((@dialogues.empty? || dialogue.has_precondition?) && dialogue.type == :npc_speech)
+ npc = dialogue.npc
+ raise 'Npc cannot be null when opening a dialogue.' if npc.nil?
+ @starters << dialogue
+
+ on :message, :first_npc_action do |ctx, player, event|
+ if npc == $world.npc_repository.get(event.index).id
+ @starters.each do |start|
+ if dialogue.precondition(player)
+ send_dialogue(player, dialogue)
+ ctx.break_handler_chain()
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+
+ # Gets part of a conversation (i.e. a dialogue).
+ def part(name)
+ raise "Conversation #{@name} does not contain a dialogue called #{name}." unless @dialogues.has_key?(name)
+ @dialogues[name]
+ end
+
end
# Declares an emote, with the specified name and id.
@@ -35,12 +74,26 @@ def declare_emote(name, id)
end
+# Sends the specified dialogue.
+def send_dialogue(player, dialogue)
+ type = dialogue.type
+ action = dialogue.action
+ action.call(player) unless action.nil?
+
+ case type
+ when :message_with_item then send_item_dialogue(player, dialogue)
+ when :message_with_model then send_model_dialogue(player, dialogue)
+ when :npc_speech then send_npc_dialogue(player, dialogue)
+ when :options then send_options_dialogue(player, dialogue)
+ when :player_speech then send_player_dialogue(player, dialogue)
+ when :text
+ if dialogue.has_continue? then send_text_dialogue(player, dialogue) else send_statement_dialogue(player, dialogue) end
+ else raise "Unrecognised dialogue type #{type}."
+ end
+end
private
-# The hash of dialogue names to dialogues.
-DIALOGUES = {}
-
# The hash of emote names to ids.
EMOTES = {}
@@ -50,8 +103,11 @@ MAXIMUM_LINE_COUNT = 4
# The maximum amount of options that can be displayed on a dialogue.
MAXIMUM_OPTION_COUNT = 5
-# The maximum width of a line, in characters.
-MAXIMUM_LINE_WIDTH = 55
+# The maximum width of a line, in pixels, for a dialogue with media.
+MAXIMUM_MEDIA_LINE_WIDTH = 350
+
+# The maximum width of a line, in pixels, for a dialogue with no media.
+MAXIMUM_LINE_WIDTH = 430
# The possible types of a dialogue.
DIALOGUE_TYPES = [ :message_with_item, :message_with_model, :npc_speech, :options, :player_speech, :text ]
@@ -61,12 +117,19 @@ class Dialogue
attr_reader :emote, :name, :media, :options, :text, :title, :type
# Initializes the Dialogue.
- def initialize(name)
+ def initialize(name, conversation)
@name = name.to_s
+ @conversation = conversation
@text = []
@options = []
end
+ # An action that is executed when the dialogue is displayed.
+ def action(&block)
+ @action = block unless block.nil?
+ @action
+ end
+
# Closes the dialogue interface when the player clicks the 'Click here to continue...' text.
def close
continue(:close => true)
@@ -77,16 +140,41 @@ class Dialogue
raise 'Cannot add a continue event on a dialogue with options.' unless @options.size.zero?
raise 'Must declare either a type or a block for a continue event.' if (type.nil? && block.nil?)
- @options << (block.nil? ? get_next_dialogue(type) : block)
+ action = get_next_dialogue(type) unless type.nil?
+ @options << ->(player) { action.call(player) unless type.nil?; block.call(player) unless block.nil? }
+ end
+
+ # Copies the value of every variable from the specified Dialogue, optionally updating the text array.
+ def copy_from(dialogue, text=nil)
+ @emote = dialogue.emote
+ @item = dialogue.item
+ @model = dialogue.model
+ @npc = dialogue.npc
+ @options = dialogue.options
+ @text = if text.nil? then dialogue.text.dup else text.dup end
+ @type = dialogue.type
end
# Sets the emote performed by the dialogue head.
def emote(emote=nil)
- raise 'Can only perform an emote on :player_speech or :npc_speech dialogues.' unless [ :npc_speech, :player_speech ].include?(@type)
- @emote = EMOTES[emote] if emote.kind_of?(Symbol)
+ unless emote.nil?
+ raise 'Can only perform an emote on :player_speech or :npc_speech dialogues.' unless [ :npc_speech, :player_speech ].include?(@type)
+ @emote = emote.kind_of?(Symbol) ? EMOTES[emote] : emote
+ end
+
@emote
end
+ # Returns whether or not this Dialogue has a continue option.
+ def has_continue?
+ !@options.empty?
+ end
+
+ # Returns whether or not this dialogue has a precondition.
+ def has_precondition?
+ !@precondition.nil?
+ end
+
# Gets the media of this dialogue.
def media()
case @type
@@ -120,8 +208,10 @@ class Dialogue
# Sets the id of the npc displayed.
def npc(npc=nil)
- raise 'Can only display an npc on :npc_speech dialogues.' unless @type == :npc_speech
- @npc = lookup_npc(npc) unless npc.nil?
+ unless npc.nil?
+ raise 'Can only display an npc on :npc_speech dialogues.' unless @type == :npc_speech
+ @npc = lookup_npc(npc)
+ end
@npc
end
@@ -136,15 +226,18 @@ class Dialogue
# Gets the array of options.
def options
- @options.dup
+ @options.dup
+ end
+
+ # Sets the precondition of this dialogue.
+ def precondition(player=nil, &block)
+ @precondition = block unless block.nil?
+ @precondition.call(player) unless player.nil?
end
# Appends a message to the text list.
def text(*message)
- unless message.nil?
- @text.concat(message)
- end
-
+ @text.concat(message) unless message.nil?
@text
end
@@ -165,47 +258,44 @@ class Dialogue
end
# Wraps text in this Dialogue, inserting extra Dialogues in the chain if necessary.
- def wrap
- lines = []
+ def wrap # TODO redo this
next if @type == :options
+ lines = []
+ maximum_width = (@type == :text) ? MAXIMUM_LINE_WIDTH : MAXIMUM_MEDIA_LINE_WIDTH
+ maximum_lines = MAXIMUM_LINE_COUNT
- text = @text[0]
- segments = []# text.chars.each_slice(MAXIMUM_LINE_WIDTH).map(&:join) # Split text into array of strings with length <= 60.
- previous = 0; index = MAXIMUM_LINE_WIDTH
+ text = @text.first
+ segments = []
+ index = 0; width = 0; space = 0
while index < text.length
- index -= 1 until text[index] == ' '
- segments << text[previous..index]
- previous = index
- index += MAXIMUM_LINE_WIDTH
- end
- segments << text[previous..text.length]
+ char = text[index]
+ space = index if char == ' '
+ width += get_width(char)
+ index += 1
- if (segments.size <= MAXIMUM_LINE_COUNT)
+ if (width >= maximum_width)
+ segments << text[0..space]
+ text = text[(space + 1)..-1]
+ width = index = space = 0
+ end
+ end
+
+ segments << text
+
+ if (segments.size <= maximum_lines)
lines.concat(segments)
- @text = @text.drop(1)
+ @text = @text[1..-1]
insert_copy(@text) if @text.size > 0
else
- remaining = MAXIMUM_LINE_COUNT - segments.size
- lines.concat(segments.first(remaining))
- insert_copy(segments.drop(remaining).join().concat(@text.drop(1)))
+ lines.concat(segments.first(maximum_lines))
+ segments = [ segments.drop(maximum_lines).join() ]
+ insert_copy(segments << @text[1..-1].join())
end
@text = lines
end
-
- # Copies the value of every variable from the specified Dialogue, optionally updating the text array.
- def copy_from(dialogue, text=nil)
- @emote = dialogue.emote
- @item = dialogue.item
- @model = dialogue.model
- @npc = dialogue.npc
- @options = dialogue.options
- @text = if text.nil? then dialogue.text.dup else text.dup end
- @type = dialogue.type
- end
-
private
# Inserts a copy of this Dialogue into the chain, but with different text.
@@ -217,22 +307,20 @@ class Dialogue
index ||= -1
name = "#{name[0..index]}-auto-inserted-#{id}"
- dialogue = Dialogue.new(name)
+ dialogue = Dialogue.new(name, @conversation)
dialogue.copy_from(self, text.dup)
dialogue.wrap()
- DIALOGUES[name] = dialogue
@options[0] = ->(player) { send_dialogue(player, dialogue) }
end
# Decodes the next dialogue interface from the hash, returning a proc.
- def get_next_dialogue(hash)
- hash.keys.each do |key|
+ def get_next_dialogue(hash) # TODO rename
+ hash.each_pair do |key, value|
case key
- when :close
- return ->(player) { player.send(CloseInterfaceMessage.new) }
- when :dialogue
- return ->(player) { send_dialogue(player, lookup_dialogue(hash[key])) }
+ when :disabled then return ->(player) { }
+ when :close then return ->(player) { player.send(CloseInterfaceMessage.new) }
+ when :dialogue then return ->(player) { send_dialogue(player, @conversation.part(value)) }
else raise "Unrecognised dialogue continue type #{key}."
end
end
@@ -240,41 +328,11 @@ class Dialogue
end
-# The existing Player class.
-class Player
+# The dialogue interface ids for dialogues that only display text, but with no 'Click here to continue...' message.
+STATEMENT_DIALOGUE_IDS = [ 12788, 12790, 12793, 12797, 6179 ] # TODO
- # Opens the dialogue with the specified name.
- def open_dialogue(name)
- dialogue = lookup_dialogue(name)
- send_dialogue(self, dialogue)
- end
-
-end
-
-
-
-# Gets a Dialogue using the name it was registered with.
-def lookup_dialogue(name)
- dialogue = DIALOGUES[name]
- raise "No dialogue named #{name.to_s}." if dialogue.nil?
-
- dialogue
-end
-
-# Sends the specified dialogue.
-def send_dialogue(player, dialogue)
- type = dialogue.type
-
- case type
- when :message_with_item then send_item_dialogue(player, dialogue)
- when :message_with_model then send_model_dialogue(player, dialogue)
- when :npc_speech then send_npc_dialogue(player, dialogue)
- when :options then send_options_dialogue(player, dialogue)
- when :player_speech then send_player_dialogue(player, dialogue)
- when :text then send_text_dialogue(player, dialogue)
- else raise "Unrecognised dialogue type #{type}."
- end
-end
+# The dialogue interface ids for dialogues that display an item and text, ordered by line count.
+ITEM_DIALOGUE_IDS = [ 306, 310, 315, 321 ]
# The dialogue interface ids for dialogues that only display text, ordered by line count.
TEXT_DIALOGUE_IDS = [ 356, 359, 363, 368, 374 ]
@@ -288,6 +346,21 @@ NPC_DIALOGUE_IDS = [ 4882, 4887, 4893, 4900 ]
# The dialogue interface ids for option dialogues, ordered by (option_count - 1)
OPTIONS_DIALOGUE_IDS = [ 2459, 2469, 2480, 2492 ]
+
+
+## TODO separate this into different Dialogue types ##
+
+
+# Sends a dialogue displaying only text, with no 'Click here to continue...' button.
+def send_statement_dialogue(player, dialogue)
+ text = dialogue.text
+ dialogue_id = STATEMENT_DIALOGUE_IDS[text.size]
+
+ set_text(player, dialogue_id + 1, dialogue.title)
+ text.each_with_index { |line, index| set_text(player, dialogue_id + 2 + index, line) }
+ player.interface_set.open_dialogue_overlay(dialogue_id)
+end
+
# Sends a dialogue displaying only text.
def send_text_dialogue(player, dialogue)
title = dialogue.title
@@ -296,7 +369,7 @@ end
# Sends a dialogue displaying the player's head.
def send_player_dialogue(player, dialogue)
- send_generic_dialogue(player, dialogue, PLAYERS_DIALOGUE_IDS, ->(id) { SetWidgetPlayerModelMessage.new(id + 1) })
+ send_generic_dialogue(player, dialogue, player.username, PLAYER_DIALOGUE_IDS, ->(id) { SetWidgetPlayerModelMessage.new(id + 1) })
end
# Sends a dialogue displaying the head of an npc.
@@ -308,7 +381,6 @@ def send_npc_dialogue(player, dialogue)
send_generic_dialogue(player, dialogue, name, NPC_DIALOGUE_IDS, ->(id) { SetWidgetNpcModelMessage.new(id + 1, npc)})
end
-
# Sends a dialogue displaying an event.
def send_generic_dialogue(player, dialogue, title, ids, event=nil)
text = dialogue.text
@@ -317,8 +389,8 @@ def send_generic_dialogue(player, dialogue, title, ids, event=nil)
set_text(player, dialogue_title_id(dialogue_id), title)
- text.each_index { |index| set_text(player, dialogue_text_id(dialogue_id, index), text[index]) }
- player.interface_set.open_dialogue(ContinueDialogueAdapter.new(player, dialogue.options[0]), dialogue_id) # TODO listener!!!
+ text.each_with_index { |line, index| set_text(player, dialogue_text_id(dialogue_id, index), line) }
+ player.interface_set.open_dialogue(ContinueDialogueAdapter.new(player, dialogue.options[0]), dialogue_id)
end
@@ -334,8 +406,8 @@ def send_options_dialogue(player, dialogue)
question = dialogue.title
set_text(player, dialogue_question_id(dialogue_id), question)
- text.each_index { |index| set_text(player, dialogue_option_id(dialogue_id, index), text[index]) }
- player.interface_set.open_dialogue(OptionDialogueAdapter.new(player, options), dialogue_id) # TODO listener!!!
+ text.each_with_index { |line, index| set_text(player, dialogue_option_id(dialogue_id, index), line) }
+ player.interface_set.open_dialogue(OptionDialogueAdapter.new(player, options), dialogue_id)
end
@@ -344,7 +416,7 @@ class ContinueDialogueAdapter < DialogueAdapter
# Creates the ContinueDialogueAdadpter.
def initialize(player, continue)
- super()
+ super()
@player = player
@continue = continue
end
@@ -362,7 +434,7 @@ class OptionDialogueAdapter < DialogueAdapter
# Creates the OptionDialogueAdadpter.
def initialize(player, options)
- super()
+ super()
@player = player
@options = options.dup
end
@@ -421,6 +493,7 @@ GLYPH_SPACING = [ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
8, 8, 8, 8, 8, 8, 8, 8, 8, 13, 6, 8, 8, 8, 8, 4, 4, 5, 4, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ]
+# Gets the width of a single character.
def get_width(char)
-
+ return GLYPH_SPACING[char.ord]
end
\ No newline at end of file
diff --git a/data/plugins/entity/attributes/attributes.rb b/data/plugins/entity/attributes/attributes.rb
index 161ac917..370593e2 100644
--- a/data/plugins/entity/attributes/attributes.rb
+++ b/data/plugins/entity/attributes/attributes.rb
@@ -1,6 +1,8 @@
require 'java'
-java_import 'org.apollo.game.model.entity.Entity'
+java_import 'org.apollo.game.model.entity.Mob'
+java_import 'org.apollo.game.model.entity.Npc'
+java_import 'org.apollo.game.model.entity.Player'
java_import 'org.apollo.game.model.entity.attr.Attribute'
java_import 'org.apollo.game.model.entity.attr.AttributeDefinition'
java_import 'org.apollo.game.model.entity.attr.AttributeMap'
@@ -10,48 +12,51 @@ java_import 'org.apollo.game.model.entity.attr.BooleanAttribute'
java_import 'org.apollo.game.model.entity.attr.NumericalAttribute'
java_import 'org.apollo.game.model.entity.attr.StringAttribute'
-class Entity
+
+# Declares an attribute and adds its definition.
+def declare_attribute(name, default, persistence=:transient)
+ raise "Attribute #{name} clashes with an existing variable." if (Player.method_defined?(name) || Mob.method_defined?(name) || Npc.method_defined?(name))
+ AttributeMap::define(name.to_s, AttributeDefinition.new(default, get_persistence(persistence), get_type(default)))
+end
+
+
+private
+
+# The existing Mob class.
+class Mob
- # Overrides method_missing
+ # Overrides method_missing to implement the functionality.
def method_missing(symbol, *args)
name = symbol.to_s.strip
if name[-1] == "="
raise "Expected argument count of 1, received #{args.length}" unless args.length == 1
- puts name
- name = name[0...-1].strip # Drop the equals
+ name = name[0...-1].strip # Drop the equals and trim whitespace.
set_attribute(name, to_attribute(args[0]))
- elsif AttributeMap::get_definition(name) == nil
+ elsif AttributeMap::get_definition(name).nil?
super(symbol, *args)
else
- attribute = get_attribute(name); definition = AttributeMap::get_definition(name)
- value = attribute == nil ? definition.default : attribute.value
-
- return definition.type == AttributeType::SYMBOL ? value.to_sym : value
- end
- end
-
- # Gets the appropriate attribute for the specified value.
- private
- def to_attribute(value)
- case value
- when String, Symbol then return StringAttribute.new(value.to_s, value.is_a?(Symbol))
- when Integer, Float then return NumericalAttribute.new(value)
- when TrueClass, FalseClass then return BooleanAttribute.new(value)
- else raise "Undefined attribute type #{value.class}."
+ attribute = get_attribute(name)
+ value = attribute.value
+ return (attribute.type == AttributeType::SYMBOL) ? value.to_sym : value
end
end
end
-# Declares an attribute and adds its definition.
-def declare_attribute(name, default, persistence=:transient)
- AttributeMap::add_definition(name.to_s, AttributeDefinition.new(default, get_persistence(persistence), get_type(default)))
+
+ # Gets the appropriate attribute for the specified value.
+def to_attribute(value)
+ case value
+ when String, Symbol then return StringAttribute.new(value.to_s, value.is_a?(Symbol))
+ when Integer, Float then return NumericalAttribute.new(value)
+ when TrueClass, FalseClass then return BooleanAttribute.new(value)
+ else raise "Undefined attribute type #{value.class}."
+ end
end
# Gets the attribute type of the specified value.
-private
def get_type(value)
case value
when String then return AttributeType::STRING
@@ -65,7 +70,7 @@ end
# Gets the Persistence type of the specified value.
def get_persistence(persistence)
- raise "Undefined persistence type #{persistence}." unless persistence == :serialized || persistence == :transient
+ raise "Undefined persistence type #{persistence}." unless [ :persistent, :transient ].include?(persistence)
- return persistence == :serialized ? AttributePersistence::SERIALIZED : AttributePersistence::TRANSIENT
+ return (persistence == :persistent) ? AttributePersistence::PERSISTENT : AttributePersistence::TRANSIENT
end
\ No newline at end of file
diff --git a/data/plugins/player-action/login.rb b/data/plugins/player-action/login.rb
new file mode 100644
index 00000000..ee2c78d3
--- /dev/null
+++ b/data/plugins/player-action/login.rb
@@ -0,0 +1,6 @@
+java_import 'org.apollo.game.model.entity.Player'
+
+on :login do |event, player|
+ show_action(player, TRADE_ACTION)
+ show_action(player, FOLLOW_ACTION)
+end
\ No newline at end of file
diff --git a/data/plugins/player-action/player-action.rb b/data/plugins/player-action/player-action.rb
new file mode 100644
index 00000000..46144a49
--- /dev/null
+++ b/data/plugins/player-action/player-action.rb
@@ -0,0 +1,59 @@
+require 'java'
+
+java_import 'org.apollo.game.message.impl.SetPlayerActionMessage'
+java_import 'org.apollo.game.model.entity.Player'
+
+class PlayerAction
+ attr_reader :slot, :primary, :name
+
+ def initialize(slot, primary, name)
+ index = [ :first, :second, :third, :fourth, :fifth ].find_index(slot)
+ raise "Unsupport action slot #{slot}." if index.nil?
+
+ @slot = index
+ @primary = primary
+ @name = name
+ end
+
+end
+
+ATTACK_ACTION = PlayerAction.new(:third, true, 'Attack')
+CHALLENGE_ACTION = PlayerAction.new(:third, true, 'Challenge')
+TRADE_ACTION = PlayerAction.new(:fourth, true, 'Trade with')
+FOLLOW_ACTION = PlayerAction.new(:fifth, true, 'Follow')
+
+# Shows multiple context menu action for the specified player
+def show_actions(player, *actions)
+ raise 'Must specify at least one action to show' if actions.nil?
+
+ actions.each do |action|
+ player.add_action(action)
+ player.send(SetPlayerActionMessage.new(action.name, action.slot, action.primary))
+ end
+end
+
+# Shows a single context menu action for the specified player
+def show_action(player, action)
+ show_actions(player, action)
+end
+
+# Hides a context menu action for the specified player
+def hide_action(player, action)
+ show_action(player, PlayerAction.new('null', action.slot, action.primary))
+end
+
+class Player
+
+ def actions
+ @actions ||= {}
+ end
+
+ def add_action(action)
+ actions[action.slot] = action.name
+ end
+
+ def has_action(action)
+ return actions[action.slot] == action.name
+ end
+
+end
\ No newline at end of file
diff --git a/data/plugins/player-action/plugin.xml b/data/plugins/player-action/plugin.xml
new file mode 100644
index 00000000..82a7d15f
--- /dev/null
+++ b/data/plugins/player-action/plugin.xml
@@ -0,0 +1,15 @@
+
+
+ player-action
+ 1
+ Player actions
+ Manages player right click actions
+
+ Ryley
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/plugins/skill/herblore/potion.rb b/data/plugins/skill/herblore/potion.rb
index 1603f0a1..b99415be 100644
--- a/data/plugins/skill/herblore/potion.rb
+++ b/data/plugins/skill/herblore/potion.rb
@@ -7,6 +7,8 @@ java_import 'org.apollo.game.model.def.ItemDefinition'
java_import 'org.apollo.game.model.inter.EnterAmountListener'
java_import 'org.apollo.game.model.inter.dialogue.DialogueAdapter'
+private
+
WATER_VIAL_ID = 227
EMPTY_VIAL_ID = 229
diff --git a/data/plugins/skill/prayer/plugin.xml b/data/plugins/skill/prayer/plugin.xml
index 3d6a402c..edbeb441 100644
--- a/data/plugins/skill/prayer/plugin.xml
+++ b/data/plugins/skill/prayer/plugin.xml
@@ -1,7 +1,7 @@
skill-prayer
- 1
+ 0.9PrayerAdds the Prayer skill.
@@ -10,6 +10,9 @@
+
-
+
+ attributes
+
\ No newline at end of file
diff --git a/data/plugins/skill/prayer/prayers.rb b/data/plugins/skill/prayer/prayers.rb
new file mode 100644
index 00000000..788b843e
--- /dev/null
+++ b/data/plugins/skill/prayer/prayers.rb
@@ -0,0 +1,89 @@
+
+java_import 'org.apollo.game.message.impl.ConfigMessage'
+
+# Declares the active prayer attribute.
+declare_attribute(:active_prayer, -1, :persistent)
+
+
+# The hash of button ids to prayers.
+PRAYERS = {}
+
+# Intercept the ButtonMessage to toggle a prayer.
+on :message, :button do |ctx, player, message|
+ button = message.widget_id
+ prayer = PRAYERS[button]
+
+ unless prayer.nil?
+ if (prayer.level > player.skill_set.get_maximum_level(Skill::PRAYER))
+ update_setting(player, prayer, :off)
+ next
+ end
+ player.send_message("after level check")
+
+ previous = player.active_prayer
+
+ unless previous == -1
+ update_setting(player, PRAYERS[previous], :off)
+ end
+
+ if previous != button
+ player.send_message("Previous: #{previous}, new: #{button}.")
+ update_setting(player, prayer, :on)
+ player.active_prayer = button
+ end
+ end
+end
+
+private
+
+# A Prayer that can be activated by a player.
+class Prayer
+ attr_reader :name, :level, :button, :setting, :drain
+
+ def initialize(name, level, button, setting, drain)
+ @name = name
+ @level = level
+ @button = button
+ @setting = setting
+ @drain = drain
+ end
+
+end
+
+def update_setting(player, prayer, state)
+ value = (state == :on) ? 1 : 0
+ player.send_message("Toggling prayer #{prayer.name}, state: #{state}.")
+ player.send(ConfigMessage.new(prayer.setting, value))
+end
+
+# Appends a Prayer to the hash.
+def append_prayer(properties)
+ raise 'Error: prayer properties hash must contain a name, level, button, setting, and drain.' unless properties.has_keys?(:name, :level, :button, :setting, :drain)
+
+ button = properties[:button]
+ PRAYERS[button] = Prayer.new(properties[:name], properties[:level], button, properties[:setting], properties[:drain])
+end
+
+# Don't deal with the actual effect here to avoid mess (TODO do it, but with attributes?).
+append_prayer name: :thick_skin, level: 1, button: 5609, setting: 83, drain: 0.01
+append_prayer name: :burst_of_strength, level: 4, button: 5610, setting: 84, drain: 0.01
+append_prayer name: :clarity_of_thought, level: 7, button: 5611, setting: 85, drain: 0.01
+append_prayer name: :rock_skin, level: 10, button: 5612, setting: 86, drain: 0.04
+append_prayer name: :superhuman_strength, level: 13, button: 5613, setting: 87, drain: 0.04
+append_prayer name: :improved_reflexes, level: 16, button: 5614, setting: 88, drain: 0.04
+
+append_prayer name: :rapid_restore, level: 19, button: 5615, setting: 89, drain: 0.01
+append_prayer name: :rapid_heal, level: 22, button: 5615, setting: 90, drain: 0.01
+append_prayer name: :protect_item, level: 25, button: 5617, setting: 91, drain: 0.01
+
+append_prayer name: :steel_skin, level: 28, button: 5618, setting: 92, drain: 0.1
+append_prayer name: :ultimate_strength, level: 31, button: 5619, setting: 93, drain: 0.1
+append_prayer name: :incredible_reflexes, level: 34, button: 5620, setting: 94, drain: 0.1
+
+append_prayer name: :protect_from_magic, level: 37, button: 5621, setting: 95, drain: 0.15
+append_prayer name: :protect_from_missiles, level: 40, button: 5622, setting: 96, drain: 0.15
+append_prayer name: :protect_from_melee, level: 43, button: 5633, setting: 97, drain: 0.15
+
+append_prayer name: :retribution, level: 46, button: 683, setting: 98, drain: 0.15
+append_prayer name: :redemption, level: 49, button: 684, setting: 99, drain: 0.15
+append_prayer name: :smite, level: 52, button: 685, setting: 100, drain: 0.2
\ No newline at end of file
diff --git a/data/plugins/skill/runecraft/tiara.rb b/data/plugins/skill/runecraft/tiara.rb
index 8a77490d..9e1559f9 100644
--- a/data/plugins/skill/runecraft/tiara.rb
+++ b/data/plugins/skill/runecraft/tiara.rb
@@ -44,22 +44,20 @@ end
# Appends a tiara to the list.
def append_tiara(hash)
- raise 'Hash must contain a tiara id, altar id, talisman id, a bitshift number, and experience.' unless hash.has_key?(:tiara_id) && hash.has_key?(:altar) && hash.has_key?(:talisman) && hash.has_key?(:bitshift) && hash.has_key?(:experience)
+ raise 'Hash must contain a tiara id, altar id, talisman id, a bitshift number, and experience.' unless hash.has_keys?(:altar, :bitshift, :experience, :talisman, :tiara_id)
tiara_id = hash[:tiara_id]; altar = hash[:altar]; talisman = hash[:talisman]; bitshift = hash[:bitshift]; experience = hash[:experience]
TIARAS_BY_TALISMAN[talisman] = TIARAS_BY_ID[tiara_id] = TIARAS_BY_ALTAR[altar] = Tiara.new(tiara_id, altar, talisman, bitshift, experience)
end
# Sets the correct config upon login, if the player is wearing a tiara.
-on :login do |player|
+on :login do |event, player|
+ player = event.player
hat = player.equipment.get(EquipmentConstants::HAT)
- next if hat.nil?
-
- tiara = TIARAS_BY_ID[hat]
- unless tiara.nil?
- tiara.send_config
- else
- send_empty_config(player)
+
+ unless hat.nil?
+ tiara = TIARAS_BY_ID[hat]
+ if tiara.nil? then send_empty_config(player) else tiara.send_config end
end
end
@@ -67,7 +65,7 @@ end
on :message, :second_object_action do |ctx, player, message|
object_id = message.id
tiara = TIARAS_BY_ALTAR[object_id]
- return if tiara.nil?
+ next if tiara.nil?
hat = player.equipment.get(EquipmentConstants::HAT)
diff --git a/data/plugins/util/command.rb b/data/plugins/util/command.rb
index 7607c062..0e86d618 100644
--- a/data/plugins/util/command.rb
+++ b/data/plugins/util/command.rb
@@ -1,3 +1,9 @@
+require 'java'
+
+java_import 'org.apollo.game.model.def.ItemDefinition'
+java_import 'org.apollo.game.model.def.NpcDefinition'
+java_import 'org.apollo.game.model.def.ObjectDefinition'
+
# Checks whether the amount of arguments provided is correct, sending the player the specified message if not.
def valid_arg_length(args, length, player, message)
diff --git a/src/org/apollo/Server.java b/src/org/apollo/Server.java
index dc97737c..4812f475 100644
--- a/src/org/apollo/Server.java
+++ b/src/org/apollo/Server.java
@@ -107,18 +107,19 @@ public final class Server {
* @param jagGrabAddress The JAGGRAB address to bind to.
*/
public void bind(SocketAddress serviceAddress, SocketAddress httpAddress, SocketAddress jagGrabAddress) {
- logger.fine("Binding service listener to address: " + serviceAddress + "...");
- serviceBootstrap.bind(serviceAddress);
-
- logger.fine("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 recommended)!", t);
- }
+ logger.fine("Binding service listener to address: " + serviceAddress + "...");
+ serviceBootstrap.bind(serviceAddress).sync();
- logger.fine("Binding JAGGRAB listener to address: " + jagGrabAddress + "...");
- jagGrabBootstrap.bind(jagGrabAddress);
+ logger.fine("Binding HTTP listener to address: " + httpAddress + "...");
+ httpBootstrap.bind(httpAddress).sync();
+
+ logger.fine("Binding JAGGRAB listener to address: " + jagGrabAddress + "...");
+ jagGrabBootstrap.bind(jagGrabAddress).sync();
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Binding to a port failed: ensure apollo isn't already running.", e);
+ System.exit(1);
+ }
logger.info("Ready for connections.");
}
diff --git a/src/org/apollo/game/GameService.java b/src/org/apollo/game/GameService.java
index 31cc3730..efdbab2a 100644
--- a/src/org/apollo/game/GameService.java
+++ b/src/org/apollo/game/GameService.java
@@ -148,14 +148,20 @@ public final class GameService extends Service {
}
/**
- * Registers a player (may block!).
+ * Registers a {@link Player} (may block!).
*
- * @param player The player.
+ * @param player The Player.
+ * @param session The {@link GameSession} of the Player.
* @return A {@link RegistrationStatus}.
*/
- public RegistrationStatus registerPlayer(Player player) {
+ public RegistrationStatus registerPlayer(Player player, GameSession session) {
synchronized (this) {
- return World.getWorld().register(player);
+ RegistrationStatus status = World.getWorld().register(player);
+ if (status == RegistrationStatus.OK) {
+ player.setSession(session);
+ }
+
+ return status;
}
}
diff --git a/src/org/apollo/game/command/CommandListener.java b/src/org/apollo/game/command/CommandListener.java
index 57eeffe6..97202fa1 100644
--- a/src/org/apollo/game/command/CommandListener.java
+++ b/src/org/apollo/game/command/CommandListener.java
@@ -1,7 +1,7 @@
package org.apollo.game.command;
import org.apollo.game.model.entity.Player;
-import org.apollo.game.model.setting.PrivilegeLevel;
+import org.apollo.game.model.entity.setting.PrivilegeLevel;
/**
* An interface which should be implemented to listen to {@link Command}s.
diff --git a/src/org/apollo/game/login/LoginDispatcher.java b/src/org/apollo/game/login/LoginDispatcher.java
deleted file mode 100644
index 878fd9c8..00000000
--- a/src/org/apollo/game/login/LoginDispatcher.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.apollo.game.login;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apollo.game.model.entity.Player;
-
-/**
- * A class that dispatches {@link Player}s to {@link LoginListener}s.
- *
- * @author Major
- */
-public final class LoginDispatcher {
-
- /**
- * A {@link List} of login listeners.
- */
- private List listeners = new ArrayList<>();
-
- /**
- * Dispatches a player to the appropriate login listener.
- *
- * @param player The player.
- */
- public void dispatch(Player player) {
- for (LoginListener listener : listeners) {
- listener.execute(player);
- }
- }
-
- /**
- * Registers a listener with this dispatcher.
- *
- * @param listener The listener.
- */
- public void register(LoginListener listener) {
- listeners.add(listener);
- }
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/game/login/LoginListener.java b/src/org/apollo/game/login/LoginListener.java
deleted file mode 100644
index 5fcc2ea4..00000000
--- a/src/org/apollo/game/login/LoginListener.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.apollo.game.login;
-
-import org.apollo.game.model.entity.Player;
-
-/**
- * A class that should be extended for actions that should be executed when the player logs in.
- *
- * @author Major
- */
-@FunctionalInterface
-public interface LoginListener {
-
- /**
- * Executes the action for this listener.
- *
- * @param player The player.
- */
- public abstract void execute(Player player);
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/game/login/LogoutDispatcher.java b/src/org/apollo/game/login/LogoutDispatcher.java
deleted file mode 100644
index c7a5e3f8..00000000
--- a/src/org/apollo/game/login/LogoutDispatcher.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.apollo.game.login;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apollo.game.model.entity.Player;
-
-/**
- * A class that dispatches {@link Player}s to {@link LogoutListener}s.
- *
- * @author Major
- */
-public final class LogoutDispatcher {
-
- /**
- * A {@link List} of logout listeners.
- */
- private List listeners = new ArrayList<>();
-
- /**
- * Dispatches a player to the appropriate logout listener.
- *
- * @param player The player.
- */
- public void dispatch(Player player) {
- for (LogoutListener listen : listeners) {
- listen.execute(player);
- }
- }
-
- /**
- * Registers a listener with this dispatcher.
- *
- * @param listener The listener.
- */
- public void register(LogoutListener listener) {
- listeners.add(listener);
- }
-
-}
diff --git a/src/org/apollo/game/login/LogoutListener.java b/src/org/apollo/game/login/LogoutListener.java
deleted file mode 100644
index 8a67f5ee..00000000
--- a/src/org/apollo/game/login/LogoutListener.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.apollo.game.login;
-
-import org.apollo.game.model.entity.Player;
-
-/**
- * An interface that should be implemented for actions that should be executed when the player logs out.
- *
- * @author Major
- */
-@FunctionalInterface
-public interface LogoutListener {
-
- /**
- * Executes the action for this listener.
- *
- * @param player The player.
- */
- public abstract void execute(Player player);
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/game/login/package-info.java b/src/org/apollo/game/login/package-info.java
deleted file mode 100644
index b157b152..00000000
--- a/src/org/apollo/game/login/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Contains login and logout listeners.
- */
-package org.apollo.game.login;
\ No newline at end of file
diff --git a/src/org/apollo/game/message/handler/MessageHandlerChain.java b/src/org/apollo/game/message/handler/MessageHandlerChain.java
index c3c9942c..96e20db5 100644
--- a/src/org/apollo/game/message/handler/MessageHandlerChain.java
+++ b/src/org/apollo/game/message/handler/MessageHandlerChain.java
@@ -1,5 +1,9 @@
package org.apollo.game.message.handler;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+
import org.apollo.game.message.Message;
import org.apollo.game.model.entity.Player;
@@ -7,6 +11,7 @@ import org.apollo.game.model.entity.Player;
* A chain of message handlers.
*
* @author Graham
+ * @author Ryley
* @param The type of message handled by this chain.
*/
public final class MessageHandlerChain {
@@ -14,7 +19,7 @@ public final class MessageHandlerChain {
/**
* The handlers.
*/
- private MessageHandler[] handlers;
+ private final Deque> handlers;
/**
* Creates the message handler chain.
@@ -23,7 +28,7 @@ public final class MessageHandlerChain {
*/
@SafeVarargs
public MessageHandlerChain(MessageHandler... handlers) {
- this.handlers = handlers;
+ this.handlers = new ArrayDeque<>(Arrays.asList(handlers));
}
/**
@@ -31,36 +36,24 @@ public final class MessageHandlerChain {
*
* @param handler The handler.
*/
- @SuppressWarnings("unchecked")
public void addLast(MessageHandler handler) {
- MessageHandler[] old = handlers;
- handlers = new MessageHandler[old.length + 1];
- System.arraycopy(old, 0, handlers, 0, old.length);
- handlers[old.length] = handler;
+ handlers.addLast(handler);
}
/**
- * Handles the message, passing it down the chain until the chain is broken or the message reaches the end of the
- * chain.
+ * Handles the message, passing it down the chain until the chain is broken
+ * or the message reaches the end of the chain.
*
* @param player The player.
* @param message The message.
*/
public void handle(Player player, M message) {
- final boolean[] running = new boolean[1];
- running[0] = true;
-
- MessageHandlerContext ctx = new MessageHandlerContext() {
-
- @Override
- public void breakHandlerChain() {
- running[0] = false;
- }
- };
+ MessageHandlerContext context = new MessageHandlerContext();
for (MessageHandler handler : handlers) {
- handler.handle(ctx, player, message);
- if (!running[0]) {
+ handler.handle(context, player, message);
+
+ if (context.isBroken()) {
break;
}
}
diff --git a/src/org/apollo/game/message/handler/MessageHandlerContext.java b/src/org/apollo/game/message/handler/MessageHandlerContext.java
index 8658e1fa..6660d1e2 100644
--- a/src/org/apollo/game/message/handler/MessageHandlerContext.java
+++ b/src/org/apollo/game/message/handler/MessageHandlerContext.java
@@ -1,15 +1,34 @@
package org.apollo.game.message.handler;
/**
- * Provides operations specific to a {@link MessageHandler} in a {@link MessageHandlerChain}.
+ * Provides operations specific to a {@link MessageHandler} in a
+ * {@link MessageHandlerChain}.
*
* @author Graham
+ * @author Ryley
*/
-public abstract class MessageHandlerContext {
+public final class MessageHandlerContext {
+
+ /**
+ * Denotes whether or not this handler chain is broken.
+ */
+ private boolean broken;
/**
* Breaks the handler chain.
*/
- public abstract void breakHandlerChain();
+ public void breakHandlerChain() {
+ broken = true;
+ }
+
+ /**
+ * Returns whether or not this handler chain is broken.
+ *
+ * @return {@code true} if this handler chain is broken, otherwise
+ * {@code false}.
+ */
+ public boolean isBroken() {
+ return broken;
+ }
}
\ No newline at end of file
diff --git a/src/org/apollo/game/message/handler/impl/EquipItemHandler.java b/src/org/apollo/game/message/handler/impl/EquipItemHandler.java
index 8142f89d..a3628e6b 100644
--- a/src/org/apollo/game/message/handler/impl/EquipItemHandler.java
+++ b/src/org/apollo/game/message/handler/impl/EquipItemHandler.java
@@ -17,7 +17,7 @@ import org.apollo.util.LanguageUtil;
*
* @author Major
* @author Graham
- * @author Ryley Kimmel
+ * @author Ryley
*/
public final class EquipItemHandler extends MessageHandler {
diff --git a/src/org/apollo/game/message/handler/impl/PlayerDesignMessageHandler.java b/src/org/apollo/game/message/handler/impl/PlayerDesignMessageHandler.java
index 96dfa71a..7e7ad3af 100644
--- a/src/org/apollo/game/message/handler/impl/PlayerDesignMessageHandler.java
+++ b/src/org/apollo/game/message/handler/impl/PlayerDesignMessageHandler.java
@@ -16,7 +16,6 @@ public final class PlayerDesignMessageHandler extends MessageHandler sectors.fromPosition(entity.getPosition()).addEntity(entity));
+ public void listenFor(Class type, EventListener listener) {
+ events.putListener(type, listener);
}
/**
@@ -348,6 +327,16 @@ public final class World {
return scheduler.schedule(task);
}
+ /**
+ * Submits the specified {@link Event}, passing it to the listeners..
+ *
+ * @param event The Event.
+ * @return {@code true} if the Event should proceed, {@code false} if not.
+ */
+ public boolean submit(Event event) {
+ return events.notify(event);
+ }
+
/**
* Unregisters the specified {@link Npc}.
*
@@ -375,11 +364,18 @@ public final class World {
Sector sector = sectors.fromPosition(player.getPosition());
sector.removeEntity(player);
-
- logoutDispatcher.dispatch(player);
} else {
logger.warning("Could not find player " + player + " to unregister!");
}
}
+ /**
+ * Adds entities to sectors in the {@link SectorRepository}.
+ *
+ * @param entities The entities.
+ */
+ private void placeEntities(Entity... entities) {
+ Arrays.stream(entities).forEach(entity -> sectors.fromPosition(entity.getPosition()).addEntity(entity));
+ }
+
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/area/Sector.java b/src/org/apollo/game/model/area/Sector.java
index e1c35d46..3c0b3e89 100644
--- a/src/org/apollo/game/model/area/Sector.java
+++ b/src/org/apollo/game/model/area/Sector.java
@@ -8,7 +8,9 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+import org.apollo.game.model.Direction;
import org.apollo.game.model.Position;
+import org.apollo.game.model.area.collision.CollisionMatrix;
import org.apollo.game.model.entity.Entity;
import org.apollo.game.model.entity.Entity.EntityType;
@@ -23,7 +25,7 @@ import com.google.common.collect.ImmutableSet;
public final class Sector {
/**
- * The width and length of a sector, in tiles.
+ * The width and length of a Sector, in tiles.
*/
public static final int SECTOR_SIZE = 8;
@@ -33,20 +35,25 @@ public final class Sector {
private static final int DEFAULT_SET_SIZE = 2;
/**
- * The sector coordinates of this sector.
+ * The SectorCoordinates of this Sector.
*/
private final SectorCoordinates coordinates;
/**
- * A map of positions to entities in that position.
+ * The Map of Positions to Entities in that Position.
*/
private final Map> entities = new HashMap<>();
/**
- * A list of listeners registered to this sector.
+ * A List of SectorListeners registered to this Sector.
*/
private final List listeners = new ArrayList<>();
+ /**
+ * The CollisionMatrix.
+ */
+ private final CollisionMatrix[] matrices = CollisionMatrix.createMatrices(Position.HEIGHT_LEVELS, SECTOR_SIZE, SECTOR_SIZE);
+
/**
* Creates a new sector.
*
@@ -67,28 +74,29 @@ public final class Sector {
}
/**
- * Adds a {@link Entity} from to sector. Note that this does not spawn the entity, or do any other action other than
+ * Adds a {@link Entity} from to sector. Note that this does not spawn the Entity, or do any other action other than
* register it to this sector.
*
- * @param entity The entity.
- * @throws IllegalArgumentException If the entity does not belong in this sector.
+ * @param entity The Entity.
+ * @throws IllegalArgumentException If the Entity does not belong in this sector.
*/
public void addEntity(Entity entity) {
Position position = entity.getPosition();
checkPosition(position);
- Set local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
+ Set local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
local.add(entity);
+
notifyListeners(entity, SectorOperation.ADD);
}
/**
- * Checks if this sector contains the specified entity.
+ * Checks if this sector contains the specified Entity.
*
* This method operates in constant time.
*
- * @param entity The entity.
- * @return {@code true} if this sector contains the entity, otherwise {@code false}.
+ * @param entity The Entity.
+ * @return {@code true} if this sector contains the Entity, otherwise {@code false}.
*/
public boolean contains(Entity entity) {
Position position = entity.getPosition();
@@ -114,12 +122,13 @@ public final class Sector {
* @return The list.
*/
public Set getEntities(Position position) {
- return ImmutableSet.copyOf(entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE)));
+ Set set = entities.get(position);
+ return (set == null) ? ImmutableSet.of() : ImmutableSet.copyOf(set);
}
/**
* Gets a shallow copy of the {@link Set} of {@link Entity}s with the specified {@link EntityType}. The returned
- * type will be immutable. Type will be inferred from the call, so ensure that the entity type and the reference
+ * type will be immutable. Type will be inferred from the call, so ensure that the Entity type and the reference
* correspond, or this method will fail at runtime.
*
* @param position The {@link Position} containing the entities.
@@ -127,21 +136,35 @@ public final class Sector {
* @return The set of entities.
*/
public Set getEntities(Position position, EntityType type) {
- Set local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
+ Set local = entities.get(position);
+ if (local == null) {
+ return ImmutableSet.of();
+ }
@SuppressWarnings("unchecked")
- Set filtered = (Set) local.stream().filter(entity -> entity.getEntityType() == type).collect(Collectors.toSet());
+ Set filtered = (Set) local.stream().filter(Entity -> Entity.getEntityType() == type).collect(Collectors.toSet());
return ImmutableSet.copyOf(filtered);
}
/**
- * Moves the {@link Entity} that was in the specified {@code old} {@link Position}, to the current position of the
- * entity.
- *
- * Both the {@code old} and current positions of the entity must belong to this sector.
+ * Gets the {@link CollisionMatrix} at the specified height level.
*
- * @param old The old position of the entity.
- * @param entity The entity to move.
+ * @param height The height level.
+ * @return The CollisionMatrix.
+ */
+ public CollisionMatrix getMatrix(int height) {
+ Preconditions.checkElementIndex(height, matrices.length, "Matrix height level must be [0, " + matrices.length + ").");
+ return matrices[height];
+ }
+
+ /**
+ * Moves the {@link Entity} that was in the specified {@code old} {@link Position}, to the current position of the
+ * Entity.
+ *
+ * Both the {@code old} and current positions of the Entity must belong to this sector.
+ *
+ * @param old The old position of the Entity.
+ * @param entity The Entity to move.
* @throws IllegalArgumentException If either of the positions do not belong to this sector.
*/
public void moveEntity(Position old, Entity entity) {
@@ -175,8 +198,8 @@ public final class Sector {
/**
* Removes a {@link Entity} from this sector.
*
- * @param entity The entity.
- * @throws IllegalArgumentException If the entity does not belong in this sector, or if it was never added.
+ * @param entity The Entity.
+ * @throws IllegalArgumentException If the Entity does not belong in this sector, or if it was never added.
*/
public void removeEntity(Entity entity) {
Position position = entity.getPosition();
@@ -191,6 +214,22 @@ public final class Sector {
notifyListeners(entity, SectorOperation.REMOVE);
}
+ /**
+ * Returns whether or not an Entity of the specified {@link EntityType type} can traverse the tile at the specified
+ * coordinate pair.
+ *
+ * @param position The {@link Position} of the tile.
+ * @param entity The {@link EntityType}.
+ * @param direction The {@link Direction} the Entity is approaching from.
+ * @return {@code true} if the tile at the specified coordinate pair is traversable, {@code false} if not.
+ */
+ public boolean traversable(Position position, EntityType entity, Direction direction) {
+ CollisionMatrix matrix = matrices[position.getHeight()];
+ int x = position.getLocalX(), y = position.getLocalY();
+
+ return matrix.traversable(x, y, entity, direction);
+ }
+
/**
* Checks that the specified {@link Position} is included in this sector.
*
diff --git a/src/org/apollo/game/model/area/collision/CollisionFlag.java b/src/org/apollo/game/model/area/collision/CollisionFlag.java
new file mode 100644
index 00000000..b129511b
--- /dev/null
+++ b/src/org/apollo/game/model/area/collision/CollisionFlag.java
@@ -0,0 +1,100 @@
+package org.apollo.game.model.area.collision;
+
+/**
+ * A type of flag in a {@link CollisionMatrix}.
+ *
+ * @author Major
+ */
+public enum CollisionFlag {
+
+ /**
+ * The walk north flag.
+ */
+ MOB_NORTH(0),
+
+ /**
+ * The walk east flag.
+ */
+ MOB_EAST(1),
+
+ /**
+ * The walk south flag.
+ */
+ MOB_SOUTH(2),
+
+ /**
+ * The walk west flag.
+ */
+ MOB_WEST(3),
+
+ /**
+ * The projectile north flag.
+ */
+ PROJECTILE_NORTH(4),
+
+ /**
+ * The projectile east flag.
+ */
+ PROJECTILE_EAST(5),
+
+ /**
+ * The projectile south flag.
+ */
+ PROJECTILE_SOUTH(6),
+
+ /**
+ * The projectile west flag.
+ */
+ PROJECTILE_WEST(7);
+
+ /**
+ * Returns an array of CollisionFlags that indicate if a Mob can traverse over a tile.
+ *
+ * @return The array of CollisionFlags.
+ */
+ public static CollisionFlag[] mobs() {
+ return new CollisionFlag[] { MOB_NORTH, MOB_EAST, MOB_SOUTH, MOB_WEST };
+ }
+
+ /**
+ * Returns an array of CollisionFlags that indicate if a Projectile can traverse over a tile.
+ *
+ * @return The array of CollisionFlags.
+ */
+ public static CollisionFlag[] projectiles() {
+ return new CollisionFlag[] { PROJECTILE_NORTH, PROJECTILE_EAST, PROJECTILE_SOUTH, PROJECTILE_WEST };
+ }
+
+ /**
+ * The index of the bit this flag is stored in.
+ */
+ private final int bit;
+
+ /**
+ * Creates the CollisionFlag.
+ *
+ * @param bit The index of the bit this flag is stored in.
+ */
+ private CollisionFlag(int bit) {
+ this.bit = bit;
+ }
+
+ /**
+ * Gets this CollisionFlag, as a {@code byte}.
+ *
+ * @return The value, as a {@code byte}.
+ */
+ public byte asByte() {
+ return (byte) (1 << bit);
+ }
+
+ /**
+ * Gets the index of the bit this flag is stored in.
+ *
+ * @return The index of the bit.
+ */
+ public int getBit() {
+ return bit;
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/area/collision/CollisionMatrix.java b/src/org/apollo/game/model/area/collision/CollisionMatrix.java
new file mode 100644
index 00000000..075c49a8
--- /dev/null
+++ b/src/org/apollo/game/model/area/collision/CollisionMatrix.java
@@ -0,0 +1,241 @@
+package org.apollo.game.model.area.collision;
+
+import java.util.Arrays;
+
+import org.apollo.game.model.Direction;
+import org.apollo.game.model.entity.Entity.EntityType;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+
+/**
+ * A 2-dimensional adjacency matrix containing tile collision data.
+ *
+ * @author Major
+ */
+public final class CollisionMatrix {
+
+ /**
+ * Creates an array of CollisionMatrix objects, all of the specified width and length.
+ *
+ * @param count The length of the array to create.
+ * @param width The width of each CollisionMatrix.
+ * @param length The length of each CollisionMatrix.
+ * @return The array of CollisionMatrix objects.
+ */
+ public static CollisionMatrix[] createMatrices(int count, int width, int length) {
+ CollisionMatrix[] matrices = new CollisionMatrix[count];
+ Arrays.setAll(matrices, index -> new CollisionMatrix(width, length));
+ return matrices;
+ }
+
+ /**
+ * Indicates that all types of traversal are allowed.
+ */
+ private static final byte ALL_ALLOWED = 0b0000_0000;
+
+ /**
+ * Indicates that no types of traversal are allowed.
+ */
+ private static final byte ALL_BLOCKED = (byte) 0b1111_1111;
+
+ /**
+ * The length of the matrix.
+ */
+ private final int length;
+
+ /**
+ * The collision matrix, as a {@code byte} array.
+ */
+ private final byte[] matrix;
+
+ /**
+ * The width of the matrix.
+ */
+ private final int width;
+
+ /**
+ * Creates the CollisionMatrix.
+ *
+ * @param width The width of the matrix.
+ * @param length The length of the matrix.
+ */
+ public CollisionMatrix(int width, int length) {
+ this.width = width;
+ this.length = length;
+ matrix = new byte[width * length];
+ }
+
+ /**
+ * Returns whether or not all of the specified {@link CollisionFlag}s are set for the specified
+ * coordinate pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param flags The CollisionFlags.
+ * @return {@code true} if all of the CollisionFlags are set, otherwise {@code false}.
+ */
+ public boolean all(int x, int y, CollisionFlag... flags) {
+ for (CollisionFlag flag : flags) {
+ if ((get(x, y) & flag.asByte()) == 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns whether or not any of the specified {@link CollisionFlag}s are set for the specified
+ * coordinate pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param flags The CollisionFlags.
+ * @return {@code true} if any of the CollisionFlags are set, otherwise {@code false}.
+ */
+ public boolean any(int x, int y, CollisionFlag... flags) {
+ for (CollisionFlag flag : flags) {
+ if ((get(x, y) & flag.asByte()) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Completely blocks the tile at the specified coordinate pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ */
+ public void block(int x, int y) {
+ set(x, y, ALL_BLOCKED);
+ }
+
+ /**
+ * Clears (i.e. sets to {@code false}) the value of the specified {@link CollisionFlag} for the specified coordinate
+ * pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param flag The CollisionFlag.
+ */
+ public void clear(int x, int y, CollisionFlag flag) {
+ set(x, y, (byte) ~flag.asByte());
+ }
+
+ /**
+ * Returns whether or not the specified {@link CollisionFlag} is set for the specified coordinate pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param flag The CollisionFlag.
+ * @return {@code true} if the CollisionFlag is set, {@code false} if not.
+ */
+ public boolean flagged(int x, int y, CollisionFlag flag) {
+ return (get(x, y) & flag.asByte()) != 0;
+ }
+
+ /**
+ * Gets the value of the specified tile.
+ *
+ * @param x The x coordinate of the tile.
+ * @param y The y coordinate of the tile.
+ * @return The value.
+ */
+ public int get(int x, int y) {
+ return matrix[indexOf(x, y)] & 0xFF;
+ }
+
+ /**
+ * Resets the cell of the specified coordinate pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ */
+ public void reset(int x, int y) {
+ set(x, y, ALL_ALLOWED);
+ }
+
+ /**
+ * Sets (i.e. sets to {@code true}) the value of the specified {@link CollisionFlag} for the specified coordinate
+ * pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param flag The CollisionFlag.
+ */
+ public void set(int x, int y, CollisionFlag flag) {
+ set(x, y, flag.asByte());
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("width", width).add("length", length).add("matrix", Arrays.toString(matrix))
+ .toString();
+ }
+
+ /**
+ * Returns whether or not an Entity of the specified {@link EntityType type} can traverse the tile at the specified
+ * coordinate pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param entity The {@link EntityType}.
+ * @param direction The {@link Direction} the Entity is approaching from.
+ * @return {@code true} if the tile at the specified coordinate pair is traversable, {@code false} if not.
+ */
+ public boolean traversable(int x, int y, EntityType entity, Direction direction) {
+ CollisionFlag[] flags = CollisionFlag.forType(entity);
+ int north = 0, east = 1, south = 2, west = 3;
+
+ switch (direction) {
+ case NORTH_WEST:
+ return any(x, y, flags[south], flags[east]);
+ case NORTH:
+ return flagged(x, y, flags[south]);
+ case NORTH_EAST:
+ return any(x, y, flags[south], flags[west]);
+ case EAST:
+ return flagged(x, y, flags[west]);
+ case SOUTH_EAST:
+ return any(x, y, flags[north], flags[west]);
+ case SOUTH:
+ return flagged(x, y, flags[north]);
+ case SOUTH_WEST:
+ return any(x, y, flags[north], flags[east]);
+ case WEST:
+ return flagged(x, y, flags[east]);
+ }
+
+ throw new IllegalArgumentException("Unrecognised direction " + direction + ".");
+ }
+
+ /**
+ * Gets the index in the matrix for the specified coordinate pair.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @return The index.
+ * @throws ArrayIndexOutOfBoundsException If the specified coordinate pair does not fit in this matrix.
+ */
+ private int indexOf(int x, int y) {
+ int index = y * width + x;
+ Preconditions.checkElementIndex(index, matrix.length, "Index out of bounds.");
+ return index;
+ }
+
+ /**
+ * Sets the appropriate index for the specified coordinate pair to the specified value.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param value The value.
+ */
+ private void set(int x, int y, byte value) {
+ matrix[indexOf(x, y)] = value;
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/area/collision/package-info.java b/src/org/apollo/game/model/area/collision/package-info.java
new file mode 100644
index 00000000..aeb5e04c
--- /dev/null
+++ b/src/org/apollo/game/model/area/collision/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains classes related to tile collision data.
+ */
+package org.apollo.game.model.area.collision;
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/Mob.java b/src/org/apollo/game/model/entity/Mob.java
index 9240dc8e..022e0692 100644
--- a/src/org/apollo/game/model/entity/Mob.java
+++ b/src/org/apollo/game/model/entity/Mob.java
@@ -16,6 +16,7 @@ import org.apollo.game.model.area.SectorRepository;
import org.apollo.game.model.def.NpcDefinition;
import org.apollo.game.model.entity.attr.Attribute;
import org.apollo.game.model.entity.attr.AttributeMap;
+import org.apollo.game.model.event.impl.MobPositionUpdateEvent;
import org.apollo.game.model.inv.Inventory;
import org.apollo.game.model.inv.Inventory.StackMode;
import org.apollo.game.model.inv.InventoryConstants;
@@ -156,7 +157,7 @@ public abstract class Mob extends Entity {
* @return The value of the attribute.
*/
public final Attribute> getAttribute(String name) {
- return attributes.getAttribute(name);
+ return attributes.get(name);
}
/**
@@ -368,7 +369,7 @@ public abstract class Mob extends Entity {
* @param value The attribute.
*/
public final void setAttribute(String name, Attribute> value) {
- attributes.setAttribute(name, value);
+ attributes.set(name, value);
}
/**
@@ -403,6 +404,15 @@ public abstract class Mob extends Entity {
}
}
+ /**
+ * Returns this mobs interacting index.
+ *
+ * @return The interaction index of this mob.
+ */
+ public int getInteractionIndex() {
+ return index;
+ }
+
/**
* Updates this mob's interacting mob.
*
@@ -410,7 +420,7 @@ public abstract class Mob extends Entity {
*/
public final void setInteractingMob(Mob mob) {
interactingMob = mob;
- blockSet.add(SynchronizationBlock.createInteractingMobBlock(mob.index));
+ blockSet.add(SynchronizationBlock.createInteractingMobBlock(mob.getInteractionIndex()));
}
/**
@@ -419,6 +429,9 @@ public abstract class Mob extends Entity {
* @param position The position.
*/
public final void setPosition(Position position) {
+ World.getWorld().submit(new MobPositionUpdateEvent(this, position));
+ // Intentionally ignore the Event result - accidentally terminating this method would break the entire server.
+
Position old = this.position;
SectorRepository repository = World.getWorld().getSectorRepository();
Sector current = repository.fromPosition(old);
diff --git a/src/org/apollo/game/model/entity/Player.java b/src/org/apollo/game/model/entity/Player.java
index 2367991e..79f9efc1 100644
--- a/src/org/apollo/game/model/entity/Player.java
+++ b/src/org/apollo/game/model/entity/Player.java
@@ -19,6 +19,17 @@ import org.apollo.game.model.Appearance;
import org.apollo.game.model.Position;
import org.apollo.game.model.World;
import org.apollo.game.model.area.Sector;
+import org.apollo.game.model.entity.attr.Attribute;
+import org.apollo.game.model.entity.attr.AttributeDefinition;
+import org.apollo.game.model.entity.attr.AttributeMap;
+import org.apollo.game.model.entity.attr.AttributePersistence;
+import org.apollo.game.model.entity.attr.NumericalAttribute;
+import org.apollo.game.model.entity.setting.MembershipStatus;
+import org.apollo.game.model.entity.setting.PrivacyState;
+import org.apollo.game.model.entity.setting.PrivilegeLevel;
+import org.apollo.game.model.entity.setting.ScreenBrightness;
+import org.apollo.game.model.event.impl.LoginEvent;
+import org.apollo.game.model.event.impl.LogoutEvent;
import org.apollo.game.model.inter.InterfaceConstants;
import org.apollo.game.model.inter.InterfaceListener;
import org.apollo.game.model.inter.InterfaceSet;
@@ -31,9 +42,6 @@ import org.apollo.game.model.inv.Inventory.StackMode;
import org.apollo.game.model.inv.InventoryConstants;
import org.apollo.game.model.inv.InventoryListener;
import org.apollo.game.model.inv.SynchronizationInventoryListener;
-import org.apollo.game.model.setting.PrivacyState;
-import org.apollo.game.model.setting.PrivilegeLevel;
-import org.apollo.game.model.setting.ScreenBrightness;
import org.apollo.game.model.skill.LevelUpSkillListener;
import org.apollo.game.model.skill.SynchronizationSkillListener;
import org.apollo.game.sync.block.SynchronizationBlock;
@@ -52,6 +60,11 @@ import com.google.common.base.Preconditions;
*/
public final class Player extends Mob {
+ static {
+ AttributeMap.define("run_energy", AttributeDefinition.forInt(100, AttributePersistence.PERSISTENT));
+ AttributeMap.define("client_version", AttributeDefinition.forInt(0, AttributePersistence.TRANSIENT));
+ }
+
/**
* The player's appearance.
*/
@@ -72,32 +85,11 @@ public final class Player extends Mob {
*/
private transient Deque clicks = new ArrayDeque<>();
- /**
- * The version of the client this player is using. This is not the same as the release number, instead denoting the
- * custom version.
- */
- private transient int clientVersion;
-
/**
* This player's credentials.
*/
private PlayerCredentials credentials;
- @Override
- public int hashCode() {
- return credentials.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof Player) {
- Player other = (Player) obj;
- return credentials.equals(other.credentials);
- }
-
- return false;
- }
-
/**
* A flag which indicates there are npcs that couldn't be added.
*/
@@ -144,14 +136,9 @@ public final class Player extends Mob {
private transient Position lastKnownSector;
/**
- * The membership flag.
+ * The MembershipStatus of this Player.
*/
- private transient boolean members = false;
-
- /**
- * A flag indicating if the player is new.
- */
- private boolean newPlayer = false;
+ private transient MembershipStatus members = MembershipStatus.FREE;
/**
* This player's prayer icon.
@@ -168,16 +155,6 @@ public final class Player extends Mob {
*/
private final transient Deque queuedMessages = new ArrayDeque<>();
- /**
- * A flag indicating if the sector changed in the last cycle.
- */
- private transient boolean sectorChanged = false;
-
- /**
- * The player's run energy.
- */
- private int runEnergy = 100;
-
/**
* A flag indicating if this player is running.
*/
@@ -188,6 +165,11 @@ public final class Player extends Mob {
*/
private ScreenBrightness screenBrightness = ScreenBrightness.NORMAL;
+ /**
+ * A flag indicating if the sector changed in the last cycle.
+ */
+ private transient boolean sectorChanged = false;
+
/**
* The {@link GameSession} currently attached to this {@link Player}.
*/
@@ -263,6 +245,16 @@ public final class Player extends Mob {
}
}
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Player) {
+ Player other = (Player) obj;
+ return credentials.equals(other.credentials);
+ }
+
+ return false;
+ }
+
/**
* Sets the excessive npcs flag.
*/
@@ -329,7 +321,8 @@ public final class Player extends Mob {
* @return The version.
*/
public int getClientVersion() {
- return clientVersion;
+ Attribute version = attributes.get("client_version");
+ return version.getValue();
}
/**
@@ -382,6 +375,11 @@ public final class Player extends Mob {
return ignores;
}
+ @Override
+ public int getInteractionIndex() {
+ return getIndex() | 0x8000;
+ }
+
/**
* Gets this player's interface set.
*
@@ -433,7 +431,8 @@ public final class Player extends Mob {
* @return The run energy.
*/
public int getRunEnergy() {
- return runEnergy;
+ Attribute energy = attributes.get("run_energy");
+ return energy.getValue();
}
/**
@@ -490,6 +489,11 @@ public final class Player extends Mob {
return worldId;
}
+ @Override
+ public int hashCode() {
+ return credentials.hashCode();
+ }
+
/**
* Indicates whether or not the player with the specified username is on this player's ignore list.
*
@@ -551,16 +555,16 @@ public final class Player extends Mob {
* @return {@code true} if so, {@code false} if not.
*/
public boolean isMembers() {
- return members;
+ return members == MembershipStatus.PAID;
}
/**
- * Checks if this player has logged in before.
+ * Gets the {@link MembershipStatus} of this Player.
*
- * @return A flag indicating if the player is new.
+ * @return The MembershipStatus.
*/
- public boolean isNew() {
- return newPlayer;
+ public MembershipStatus getMembershipStatus() {
+ return members;
}
/**
@@ -594,7 +598,9 @@ public final class Player extends Mob {
* Logs the player out, if possible.
*/
public void logout() {
- send(new LogoutMessage());
+ if (World.getWorld().submit(new LogoutEvent(this))) {
+ send(new LogoutMessage());
+ }
}
/**
@@ -675,6 +681,27 @@ public final class Player extends Mob {
}
}
+ /**
+ * Sends the initial messages.
+ */
+ public void sendInitialMessages() {
+ blockSet.add(SynchronizationBlock.createAppearanceBlock(this));
+ send(new IdAssignmentMessage(index, members)); // TODO should this be sent when we reconnect?
+ sendMessage("Welcome to RuneScape.");
+
+ int[] tabs = InterfaceConstants.DEFAULT_INVENTORY_TABS;
+ for (int tab = 0; tab < tabs.length; tab++) {
+ send(new SwitchTabInterfaceMessage(tab, tabs[tab]));
+ }
+
+ inventory.forceRefresh();
+ equipment.forceRefresh();
+ bank.forceRefresh();
+ skillSet.forceRefresh();
+
+ World.getWorld().submit(new LoginEvent(this));
+ }
+
/**
* Sends a message to the player.
*
@@ -691,7 +718,7 @@ public final class Player extends Mob {
* @param filterable Whether or not the message can be filtered.
*/
public void sendMessage(String message, boolean filterable) {
- if (clientVersion > 0) {
+ if (getClientVersion() > 0) {
send(new ServerChatMessage(message, filterable));
} else if (!filterable || !filteringMessages) {
send(new ServerChatMessage(message));
@@ -748,12 +775,12 @@ public final class Player extends Mob {
}
/**
- * Sets the value denoting the client's modified version. TODO make this an attribute?
+ * Sets the value denoting the client's modified version.
*
- * @param clientVersion The client version.
+ * @param version The client version.
*/
- public void setClientVersion(int clientVersion) {
- this.clientVersion = clientVersion;
+ public void setClientVersion(int version) {
+ attributes.set("client_version", new NumericalAttribute(version));
}
/**
@@ -797,19 +824,10 @@ public final class Player extends Mob {
*
* @param members The new membership flag.
*/
- public void setMembers(boolean members) {
+ public void setMembers(MembershipStatus members) {
this.members = members;
}
- /**
- * Sets the new player flag. TODO make this an attribute?
- *
- * @param newPlayer A flag indicating if the player has played before.
- */
- public void setNew(boolean newPlayer) {
- this.newPlayer = newPlayer;
- }
-
/**
* Sets the player's prayer icon. TODO make this an attribute?
*
@@ -829,22 +847,13 @@ public final class Player extends Mob {
}
/**
- * Sets the sector changed flag.
+ * Sets the player's run energy.
*
- * @param sectorChanged A flag indicating if the sector has changed.
+ * @param energy The energy.
*/
- public void setSectorChanged(boolean sectorChanged) {
- this.sectorChanged = sectorChanged;
- }
-
- /**
- * Sets the player's run energy. TODO make this an attribute?
- *
- * @param runEnergy The energy.
- */
- public void setRunEnergy(int runEnergy) {
- this.runEnergy = runEnergy;
- send(new UpdateRunEnergyMessage(runEnergy));
+ public void setRunEnergy(int energy) {
+ attributes.set("run_energy", new NumericalAttribute(energy));
+ send(new UpdateRunEnergyMessage(energy));
}
/**
@@ -856,18 +865,22 @@ public final class Player extends Mob {
this.screenBrightness = brightness;
}
+ /**
+ * Sets the sector changed flag.
+ *
+ * @param sectorChanged A flag indicating if the sector has changed.
+ */
+ public void setSectorChanged(boolean sectorChanged) {
+ this.sectorChanged = sectorChanged;
+ }
+
/**
* Sets the player's {@link GameSession}.
*
* @param session The player's {@link GameSession}.
- * @param reconnecting The reconnecting flag.
*/
- public void setSession(GameSession session, boolean reconnecting) {
+ public void setSession(GameSession session) {
this.session = session;
- if (!reconnecting) {
- sendInitialMessages();
- }
- blockSet.add(SynchronizationBlock.createAppearanceBlock(this));
}
/**
@@ -931,7 +944,7 @@ public final class Player extends Mob {
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("username", getUsername()).add("privilege", privilegeLevel)
- .add("client version", clientVersion).toString();
+ .add("client version", getClientVersion()).toString();
}
/**
@@ -977,27 +990,4 @@ public final class Player extends Mob {
skillSet.addListener(new LevelUpSkillListener(this));
}
- /**
- * Sends the initial messages.
- */
- private void sendInitialMessages() {
- send(new IdAssignmentMessage(index, members)); // TODO should this be sent when we reconnect?
- sendMessage("Welcome to RuneScape.");
- if (!newPlayer) {
- interfaceSet.openWindow(InterfaceConstants.AVATAR_DESIGN);
- }
-
- int[] tabs = InterfaceConstants.DEFAULT_INVENTORY_TABS;
- for (int tab = 0; tab < tabs.length; tab++) {
- send(new SwitchTabInterfaceMessage(tab, tabs[tab]));
- }
-
- inventory.forceRefresh();
- equipment.forceRefresh();
- bank.forceRefresh();
- skillSet.forceRefresh();
-
- World.getWorld().getLoginDispatcher().dispatch(this);
- }
-
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/attr/Attribute.java b/src/org/apollo/game/model/entity/attr/Attribute.java
index 9fff080f..6627b8a0 100644
--- a/src/org/apollo/game/model/entity/attr/Attribute.java
+++ b/src/org/apollo/game/model/entity/attr/Attribute.java
@@ -13,12 +13,12 @@ public abstract class Attribute {
/**
* The type of this attribute.
*/
- private final AttributeType type;
+ protected final AttributeType type;
/**
* The value of this attribute.
*/
- protected T value;
+ protected final T value;
/**
* Creates the attribute with the specified {@link AttributeType} and value.
@@ -31,6 +31,13 @@ public abstract class Attribute {
this.value = value;
}
+ /**
+ * Encodes this Attribute into a byte array.
+ *
+ * @return The byte array.
+ */
+ public abstract byte[] encode();
+
/**
* Gets the type of this attribute.
*
@@ -49,4 +56,11 @@ public abstract class Attribute {
return value;
}
+ /**
+ * Returns the Sting representation of this Attribute. Will be used to write this Attribute as a String, if
+ * required.
+ */
+ @Override
+ public abstract String toString();
+
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/attr/AttributeDefinition.java b/src/org/apollo/game/model/entity/attr/AttributeDefinition.java
index 9a48b15d..0f4092b1 100644
--- a/src/org/apollo/game/model/entity/attr/AttributeDefinition.java
+++ b/src/org/apollo/game/model/entity/attr/AttributeDefinition.java
@@ -10,25 +10,69 @@ package org.apollo.game.model.entity.attr;
public final class AttributeDefinition {
/**
- * The default value of this definition.
+ * Creates an AttributeDefinition for a {@code boolean}.
+ *
+ * @param defaultValue The default value of the definition.
+ * @param persistence The {@link AttributePersistence} of the definition.
+ * @return The AttributeDefinition.
+ */
+ public static AttributeDefinition forBoolean(boolean defaultValue, AttributePersistence persistence) {
+ return new AttributeDefinition<>(defaultValue, persistence, AttributeType.BOOLEAN);
+ }
+
+ /**
+ * Creates an AttributeDefinition for a {@code double}.
+ *
+ * @param defaultValue The default value of the definition.
+ * @param persistence The {@link AttributePersistence} of the definition.
+ * @return The AttributeDefinition.
+ */
+ public static AttributeDefinition forDouble(double defaultValue, AttributePersistence persistence) {
+ return new AttributeDefinition<>(defaultValue, persistence, AttributeType.DOUBLE);
+ }
+
+ /**
+ * Creates an AttributeDefinition for an {@code int}.
+ *
+ * @param defaultValue The default value of the definition.
+ * @param persistence The {@link AttributePersistence} of the definition.
+ * @return The AttributeDefinition.
+ */
+ public static AttributeDefinition forInt(int defaultValue, AttributePersistence persistence) {
+ return new AttributeDefinition<>(defaultValue, persistence, AttributeType.LONG);
+ }
+
+ /**
+ * Creates an AttributeDefinition for a String.
+ *
+ * @param defaultValue The default value of the definition.
+ * @param persistence The {@link AttributePersistence} of the definition.
+ * @return The AttributeDefinition.
+ */
+ public static AttributeDefinition forString(String defaultValue, AttributePersistence persistence) {
+ return new AttributeDefinition<>(defaultValue, persistence, AttributeType.STRING);
+ }
+
+ /**
+ * The default value of the Attribute.
*/
private final T defaultValue;
/**
- * The persistence state of this definition.
+ * The persistence state of the Attribute.
*/
private final AttributePersistence persistence;
/**
- * The type of this definition.
+ * The type of the Attribute.
*/
private final AttributeType type;
/**
- * Creates the attribute definition.
+ * Creates the AttributeDefinition.
*
* @param defaultValue The default value.
- * @param persistence The {@link AttributePersistence} state.
+ * @param persistence The {@link AttributePersistence}.
* @param type The {@link AttributeType}.
*/
public AttributeDefinition(T defaultValue, AttributePersistence persistence, AttributeType type) {
@@ -38,7 +82,7 @@ public final class AttributeDefinition {
}
/**
- * Gets the default value of this attribute definition.
+ * Gets the default value of this AttributeDefinition.
*
* @return The default value.
*/
@@ -47,18 +91,18 @@ public final class AttributeDefinition {
}
/**
- * Gets the persistence state of this attribute definition.
+ * Gets the {@link AttributePersistence} of this AttributeDefinition.
*
- * @return The persistence.
+ * @return The AttributePersistence.
*/
public AttributePersistence getPersistence() {
return persistence;
}
/**
- * Gets the {@link AttributeType} of this definition.
+ * Gets the {@link AttributeType} of this AttributeDefinition
*
- * @return The attribute type.
+ * @return The AttributeType.
*/
public AttributeType getType() {
return type;
diff --git a/src/org/apollo/game/model/entity/attr/AttributeMap.java b/src/org/apollo/game/model/entity/attr/AttributeMap.java
index 0d79d68e..e1f0ce9b 100644
--- a/src/org/apollo/game/model/entity/attr/AttributeMap.java
+++ b/src/org/apollo/game/model/entity/attr/AttributeMap.java
@@ -3,6 +3,8 @@ package org.apollo.game.model.entity.attr;
import java.util.HashMap;
import java.util.Map;
+import org.jruby.RubySymbol;
+
import com.google.common.base.Preconditions;
/**
@@ -12,10 +14,15 @@ import com.google.common.base.Preconditions;
*/
public final class AttributeMap {
+ /**
+ * The default size of the map.
+ */
+ private static final int DEFAULT_MAP_SIZE = 2;
+
/**
* The map of attribute names to definitions.
*/
- private static Map> definitions = new HashMap<>(1);
+ private static Map> definitions = new HashMap<>();
/**
* Registers an {@link AttributeDefinition}.
@@ -23,7 +30,7 @@ public final class AttributeMap {
* @param name The name of the attribute.
* @param definition The definition.
*/
- public static void addDefinition(String name, AttributeDefinition> definition) {
+ public static void define(String name, AttributeDefinition> definition) {
definitions.put(name, definition);
}
@@ -33,8 +40,9 @@ public final class AttributeMap {
* @param name The name of the attribute.
* @return The attribute definition.
*/
- public static AttributeDefinition> getDefinition(String name) {
- return definitions.get(name);
+ @SuppressWarnings("unchecked")
+ public static AttributeDefinition getDefinition(String name) {
+ return (AttributeDefinition) definitions.get(name);
}
/**
@@ -46,10 +54,20 @@ public final class AttributeMap {
return new HashMap<>(definitions);
}
+ /**
+ * Returns whether or not an {@link AttributeDefinition} with the specified name exists.
+ *
+ * @param name The name of the AttributeDefinition.
+ * @return {@code true} if the AttributeDefinition exists, {@code false} if not.
+ */
+ public static boolean hasDefinition(String name) {
+ return definitions.containsKey(name);
+ }
+
/**
* The map of attribute names to attributes.
*/
- private Map> attributes = new HashMap<>();
+ private Map> attributes = new HashMap<>(DEFAULT_MAP_SIZE);
/**
* Gets the {@link Attribute} with the specified name.
@@ -57,8 +75,13 @@ public final class AttributeMap {
* @param name The name of the attribute.
* @return The attribute.
*/
- public Attribute> getAttribute(String name) {
- return attributes.get(name);
+ @SuppressWarnings("unchecked")
+ public Attribute get(String name) {
+ AttributeDefinition definition = getDefinition(name);
+ Preconditions.checkNotNull(definition, "Attributes must be defined before their value can be retreived.");
+
+ return (Attribute) attributes.computeIfAbsent(name,
+ key -> createAttribute(definition.getDefault(), definition.getType()));
}
/**
@@ -76,9 +99,32 @@ public final class AttributeMap {
* @param name The name of the attribute.
* @param attribute The attribute.
*/
- public void setAttribute(String name, Attribute> attribute) {
+ public void set(String name, Attribute> attribute) {
Preconditions.checkNotNull(getDefinition(name), "Attributes must be defined before their value can be set.");
attributes.put(name, attribute);
}
+ /**
+ * Creates an {@link Attribute} with the specified value and {@link AttributeType}.
+ *
+ * @param value The value of the Attribute.
+ * @param type The AttributeType.
+ * @return The Attribute.
+ */
+ private Attribute> createAttribute(T value, AttributeType type) {
+ switch (type) {
+ case LONG:
+ case DOUBLE:
+ return new NumericalAttribute((Integer) value);
+ case STRING:
+ return new StringAttribute((String) value);
+ case SYMBOL:
+ return new StringAttribute(((RubySymbol) value).asJavaString(), true);
+ case BOOLEAN:
+ return new BooleanAttribute((Boolean) value);
+ }
+
+ throw new IllegalArgumentException("Unrecognised type " + type + ".");
+ }
+
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/attr/AttributePersistence.java b/src/org/apollo/game/model/entity/attr/AttributePersistence.java
index 29cb365f..5c86de00 100644
--- a/src/org/apollo/game/model/entity/attr/AttributePersistence.java
+++ b/src/org/apollo/game/model/entity/attr/AttributePersistence.java
@@ -8,7 +8,7 @@ public enum AttributePersistence {
/**
* The serialized persistence type, indicating that the attribute will be saved.
*/
- SERIALIZED,
+ PERSISTENT,
/**
* The transient persistence type, indicating that the attribute will not be saved.
diff --git a/src/org/apollo/game/model/entity/attr/BooleanAttribute.java b/src/org/apollo/game/model/entity/attr/BooleanAttribute.java
index e5409c66..e9df53d1 100644
--- a/src/org/apollo/game/model/entity/attr/BooleanAttribute.java
+++ b/src/org/apollo/game/model/entity/attr/BooleanAttribute.java
@@ -16,4 +16,14 @@ public final class BooleanAttribute extends Attribute {
super(AttributeType.BOOLEAN, value);
}
+ @Override
+ public byte[] encode() {
+ return new byte[] { (byte) (value ? 1 : 0) };
+ }
+
+ @Override
+ public String toString() {
+ return Boolean.toString(value);
+ }
+
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/attr/NumericalAttribute.java b/src/org/apollo/game/model/entity/attr/NumericalAttribute.java
index a7f21982..ffc3915b 100644
--- a/src/org/apollo/game/model/entity/attr/NumericalAttribute.java
+++ b/src/org/apollo/game/model/entity/attr/NumericalAttribute.java
@@ -1,5 +1,7 @@
package org.apollo.game.model.entity.attr;
+import com.google.common.primitives.Longs;
+
/**
* An {@link Attribute} with a numerical value.
*
@@ -26,4 +28,15 @@ public final class NumericalAttribute extends Attribute {
super(typeOf(value), value);
}
+ @Override
+ public byte[] encode() {
+ long encoded = (type == AttributeType.DOUBLE) ? Double.doubleToLongBits((double) value) : (long) value;
+ return Longs.toByteArray(encoded);
+ }
+
+ @Override
+ public String toString() {
+ return (type == AttributeType.DOUBLE) ? Double.toString((double) value) : Long.toString((long) value);
+ }
+
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/attr/StringAttribute.java b/src/org/apollo/game/model/entity/attr/StringAttribute.java
index be7c763c..6468ee11 100644
--- a/src/org/apollo/game/model/entity/attr/StringAttribute.java
+++ b/src/org/apollo/game/model/entity/attr/StringAttribute.java
@@ -1,5 +1,7 @@
package org.apollo.game.model.entity.attr;
+import java.nio.charset.Charset;
+
/**
* An {@link Attribute} with a string value.
*
@@ -26,4 +28,14 @@ public final class StringAttribute extends Attribute {
super(symbol ? AttributeType.SYMBOL : AttributeType.STRING, value);
}
+ @Override
+ public byte[] encode() {
+ return value.getBytes(Charset.forName("UTF-8"));
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/setting/Gender.java b/src/org/apollo/game/model/entity/setting/Gender.java
similarity index 91%
rename from src/org/apollo/game/model/setting/Gender.java
rename to src/org/apollo/game/model/entity/setting/Gender.java
index 1e95bae1..9cd43668 100644
--- a/src/org/apollo/game/model/setting/Gender.java
+++ b/src/org/apollo/game/model/entity/setting/Gender.java
@@ -1,4 +1,4 @@
-package org.apollo.game.model.setting;
+package org.apollo.game.model.entity.setting;
/**
* An enumeration containing the two genders (male and female). This enumeration relies on the ordering of the elements
diff --git a/src/org/apollo/game/model/entity/setting/MembershipStatus.java b/src/org/apollo/game/model/entity/setting/MembershipStatus.java
new file mode 100644
index 00000000..c02f86f3
--- /dev/null
+++ b/src/org/apollo/game/model/entity/setting/MembershipStatus.java
@@ -0,0 +1,58 @@
+package org.apollo.game.model.entity.setting;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * The membership status of a Player.
+ *
+ * @author Major
+ */
+public enum MembershipStatus {
+
+ /**
+ * The free membership status.
+ */
+ FREE(0),
+
+ /**
+ * The paid membership status.
+ */
+ PAID(1);
+
+ /**
+ * Gets the MembershipStatus with the specified value.
+ *
+ * @param value The integer value of the MembershipStatus.
+ * @return The MembershipStatus.
+ * @throws IllegalArgumentException If no MembershipStatus with the specified the value exists.
+ */
+ public static MembershipStatus valueOf(int value) {
+ Optional optional = Arrays.stream(values()).filter(status -> status.value == value).findAny();
+ return optional.orElseThrow(() -> new IllegalArgumentException("Illegal membership status value."));
+ }
+
+ /**
+ * The integer value of this MembershipStatus.
+ */
+ private final int value;
+
+ /**
+ * Creates the MembershipStatus.
+ *
+ * @param value The integer value.
+ */
+ private MembershipStatus(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the value of this MembershipStatus.
+ *
+ * @return The value.
+ */
+ public int getValue() {
+ return value;
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/setting/PrivacyState.java b/src/org/apollo/game/model/entity/setting/PrivacyState.java
similarity index 98%
rename from src/org/apollo/game/model/setting/PrivacyState.java
rename to src/org/apollo/game/model/entity/setting/PrivacyState.java
index 0a62b1fc..76fbec8d 100644
--- a/src/org/apollo/game/model/setting/PrivacyState.java
+++ b/src/org/apollo/game/model/entity/setting/PrivacyState.java
@@ -1,4 +1,4 @@
-package org.apollo.game.model.setting;
+package org.apollo.game.model.entity.setting;
import com.google.common.base.Preconditions;
diff --git a/src/org/apollo/game/model/setting/PrivilegeLevel.java b/src/org/apollo/game/model/entity/setting/PrivilegeLevel.java
similarity index 96%
rename from src/org/apollo/game/model/setting/PrivilegeLevel.java
rename to src/org/apollo/game/model/entity/setting/PrivilegeLevel.java
index e5738f7c..fbdaf7c6 100644
--- a/src/org/apollo/game/model/setting/PrivilegeLevel.java
+++ b/src/org/apollo/game/model/entity/setting/PrivilegeLevel.java
@@ -1,4 +1,4 @@
-package org.apollo.game.model.setting;
+package org.apollo.game.model.entity.setting;
import com.google.common.base.Preconditions;
diff --git a/src/org/apollo/game/model/setting/ScreenBrightness.java b/src/org/apollo/game/model/entity/setting/ScreenBrightness.java
similarity index 96%
rename from src/org/apollo/game/model/setting/ScreenBrightness.java
rename to src/org/apollo/game/model/entity/setting/ScreenBrightness.java
index 9ae6cd9c..4a190a44 100644
--- a/src/org/apollo/game/model/setting/ScreenBrightness.java
+++ b/src/org/apollo/game/model/entity/setting/ScreenBrightness.java
@@ -1,4 +1,4 @@
-package org.apollo.game.model.setting;
+package org.apollo.game.model.entity.setting;
import com.google.common.base.Preconditions;
diff --git a/src/org/apollo/game/model/setting/ServerStatus.java b/src/org/apollo/game/model/entity/setting/ServerStatus.java
similarity index 95%
rename from src/org/apollo/game/model/setting/ServerStatus.java
rename to src/org/apollo/game/model/entity/setting/ServerStatus.java
index 05bc3d8e..651d4b88 100644
--- a/src/org/apollo/game/model/setting/ServerStatus.java
+++ b/src/org/apollo/game/model/entity/setting/ServerStatus.java
@@ -1,4 +1,4 @@
-package org.apollo.game.model.setting;
+package org.apollo.game.model.entity.setting;
import com.google.common.base.Preconditions;
diff --git a/src/org/apollo/game/model/setting/package-info.java b/src/org/apollo/game/model/entity/setting/package-info.java
similarity index 60%
rename from src/org/apollo/game/model/setting/package-info.java
rename to src/org/apollo/game/model/entity/setting/package-info.java
index 5c8ed99e..db35d20a 100644
--- a/src/org/apollo/game/model/setting/package-info.java
+++ b/src/org/apollo/game/model/entity/setting/package-info.java
@@ -1,4 +1,4 @@
/**
* Contains player setting or customisation-related classes.
*/
-package org.apollo.game.model.setting;
\ No newline at end of file
+package org.apollo.game.model.entity.setting;
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/Event.java b/src/org/apollo/game/model/event/Event.java
new file mode 100644
index 00000000..8d71d84e
--- /dev/null
+++ b/src/org/apollo/game/model/event/Event.java
@@ -0,0 +1,31 @@
+package org.apollo.game.model.event;
+
+/**
+ * A type of event that may occur in the game world.
+ *
+ * @author Major
+ */
+public abstract class Event {
+
+ /**
+ * Indicates whether or not the Event chain has been terminated.
+ */
+ private boolean terminated;
+
+ /**
+ * Terminates the Event chain.
+ */
+ public final void terminate() {
+ terminated = true;
+ }
+
+ /**
+ * Returns whether or not the Event chain has been terminated.
+ *
+ * @return {@code true} if the Event chain has been terminated, otherwise {@code false}.
+ */
+ public final boolean terminated() {
+ return terminated;
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/EventListener.java b/src/org/apollo/game/model/event/EventListener.java
new file mode 100644
index 00000000..7a5306b8
--- /dev/null
+++ b/src/org/apollo/game/model/event/EventListener.java
@@ -0,0 +1,20 @@
+package org.apollo.game.model.event;
+
+/**
+ * A listener for an {@link Event} that may occur in the game world.
+ *
+ * @author Major
+ *
+ * @param The type of Event.
+ */
+@FunctionalInterface
+public interface EventListener {
+
+ /**
+ * Handles the {@link Event} that occurred.
+ *
+ * @param event The Event.
+ */
+ public void handle(E event);
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/EventListenerChain.java b/src/org/apollo/game/model/event/EventListenerChain.java
new file mode 100644
index 00000000..a2fbca2d
--- /dev/null
+++ b/src/org/apollo/game/model/event/EventListenerChain.java
@@ -0,0 +1,66 @@
+package org.apollo.game.model.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * A chain of {@link EventListener}s.
+ *
+ * @author Major
+ * @param The type of {@link Event} the listeners in this chain listen for.
+ */
+final class EventListenerChain {
+
+ /**
+ * The List of EventListeners.
+ */
+ private final List> listeners = new ArrayList<>();
+
+ /**
+ * The Class type of this chain.
+ */
+ private final Class type;
+
+ /**
+ * Creates the EventListenerChain.
+ *
+ * @param type The {@link Class} type of this chain.
+ */
+ public EventListenerChain(Class type) {
+ this.type = type;
+ }
+
+ /**
+ * Adds an {@link EventListener} to this chain.
+ *
+ * @param listener The EventListener to add.
+ */
+ public void addListener(EventListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Notifies each {@link EventListener} in this chain that an {@link Event} has occurred.
+ *
+ * @param event The event.
+ * @return {@code true} if the Event should continue on with its outcome, {@code false} if not.
+ */
+ public boolean notify(E event) {
+ for (EventListener listener : listeners) {
+ listener.handle(event);
+
+ if (event.terminated()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("type", type).add("listeners", listeners).toString();
+ }
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/EventListenerChainSet.java b/src/org/apollo/game/model/event/EventListenerChainSet.java
new file mode 100644
index 00000000..8dbe8bfa
--- /dev/null
+++ b/src/org/apollo/game/model/event/EventListenerChainSet.java
@@ -0,0 +1,42 @@
+package org.apollo.game.model.event;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A set of {@link EventListenerChain}s.
+ *
+ * @author Major
+ */
+public final class EventListenerChainSet {
+
+ /**
+ * The Map of Event Classes to EventListenerChains.
+ */
+ private final Map, EventListenerChain extends Event>> chains = new HashMap<>();
+
+ /**
+ * Notifies the appropriate {@link EventListenerChain} that an {@link Event} has occurred.
+ *
+ * @param event The Event.
+ * @return {@code true} if the Event should continue on with its outcome, {@code false} if not.
+ */
+ public boolean notify(E event) {
+ @SuppressWarnings("unchecked")
+ EventListenerChain chain = (EventListenerChain) chains.get(event.getClass());
+ return (chain == null) ? true : chain.notify(event);
+ }
+
+ /**
+ * Places the {@link EventListenerChain} into this set.
+ *
+ * @param clazz The {@link Class} to associate the EventListenerChain with.
+ * @param listener The EventListenerChain.
+ */
+ public void putListener(Class clazz, EventListener listener) {
+ @SuppressWarnings("unchecked")
+ EventListenerChain chain = (EventListenerChain) chains.computeIfAbsent(clazz, EventListenerChain::new);
+ chain.addListener(listener);
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/PlayerEvent.java b/src/org/apollo/game/model/event/PlayerEvent.java
new file mode 100644
index 00000000..23272210
--- /dev/null
+++ b/src/org/apollo/game/model/event/PlayerEvent.java
@@ -0,0 +1,35 @@
+package org.apollo.game.model.event;
+
+import org.apollo.game.model.entity.Player;
+
+/**
+ * An {@link Event} involving a {@link Player}.
+ *
+ * @author Major
+ */
+public abstract class PlayerEvent extends Event {
+
+ /**
+ * The Player.
+ */
+ private final Player player;
+
+ /**
+ * Creates the PlayerEvent.
+ *
+ * @param player The {@link Player}.
+ */
+ public PlayerEvent(Player player) {
+ this.player = player;
+ }
+
+ /**
+ * Gets the {@link Player}.
+ *
+ * @return The Player.
+ */
+ public Player getPlayer() {
+ return player;
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/impl/CloseInterfacesEvent.java b/src/org/apollo/game/model/event/impl/CloseInterfacesEvent.java
new file mode 100644
index 00000000..c2f08c74
--- /dev/null
+++ b/src/org/apollo/game/model/event/impl/CloseInterfacesEvent.java
@@ -0,0 +1,23 @@
+package org.apollo.game.model.event.impl;
+
+import org.apollo.game.model.entity.Player;
+import org.apollo.game.model.event.Event;
+import org.apollo.game.model.event.PlayerEvent;
+
+/**
+ * An {@link Event} indicating that a player's open interfaces are about to be closed.
+ *
+ * @author Major
+ */
+public final class CloseInterfacesEvent extends PlayerEvent {
+
+ /**
+ * Creates the CloseInterfacesEvent.
+ *
+ * @param player The {@link Player} whose interfaces are being closed.
+ */
+ public CloseInterfacesEvent(Player player) {
+ super(player);
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/impl/LoginEvent.java b/src/org/apollo/game/model/event/impl/LoginEvent.java
new file mode 100644
index 00000000..307764f4
--- /dev/null
+++ b/src/org/apollo/game/model/event/impl/LoginEvent.java
@@ -0,0 +1,22 @@
+package org.apollo.game.model.event.impl;
+
+import org.apollo.game.model.entity.Player;
+import org.apollo.game.model.event.PlayerEvent;
+
+/**
+ * A {@link PlayerEvent} that is fired when a {@link Player} logs in.
+ *
+ * @author Major
+ */
+public final class LoginEvent extends PlayerEvent {
+
+ /**
+ * Creates the LoginEvent.
+ *
+ * @param player The {@link Player} logging in.
+ */
+ public LoginEvent(Player player) {
+ super(player);
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/impl/LogoutEvent.java b/src/org/apollo/game/model/event/impl/LogoutEvent.java
new file mode 100644
index 00000000..16f17390
--- /dev/null
+++ b/src/org/apollo/game/model/event/impl/LogoutEvent.java
@@ -0,0 +1,22 @@
+package org.apollo.game.model.event.impl;
+
+import org.apollo.game.model.entity.Player;
+import org.apollo.game.model.event.PlayerEvent;
+
+/**
+ * A {@link PlayerEvent} that is fired when a {@link Player} logs out.
+ *
+ * @author Major
+ */
+public final class LogoutEvent extends PlayerEvent {
+
+ /**
+ * Creates the LogoutEvent.
+ *
+ * @param player The {@link Player} logging out.
+ */
+ public LogoutEvent(Player player) {
+ super(player);
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/impl/MobPositionUpdateEvent.java b/src/org/apollo/game/model/event/impl/MobPositionUpdateEvent.java
new file mode 100644
index 00000000..82f9a470
--- /dev/null
+++ b/src/org/apollo/game/model/event/impl/MobPositionUpdateEvent.java
@@ -0,0 +1,56 @@
+package org.apollo.game.model.event.impl;
+
+import org.apollo.game.model.Position;
+import org.apollo.game.model.entity.Mob;
+import org.apollo.game.model.event.Event;
+
+/**
+ * An {@link Event} created when a Mob's Position is being updated.
+ *
+ * This Event intentionally ignores the result of execution - it should not be possible for a plugin to prevent this
+ * Event from happening, only to listen for it.
+ *
+ * @author Major
+ */
+public final class MobPositionUpdateEvent extends Event {
+
+ /**
+ * The Mob whose position is being updated.
+ */
+ private final Mob mob;
+
+ /**
+ * The next Position of the Mob.
+ */
+ private final Position next;
+
+ /**
+ * Creates the MobPositionUpdateEvent.
+ *
+ * @param mob The {@link Mob} whose Position is being updated.
+ * @param next The next {@link Position} of the Mob.
+ */
+ public MobPositionUpdateEvent(Mob mob, Position next) {
+ this.mob = mob;
+ this.next = next;
+ }
+
+ /**
+ * Gets the {@link Mob} being moved.
+ *
+ * @return The Mob.
+ */
+ public Mob getMob() {
+ return mob;
+ }
+
+ /**
+ * Gets the {@link Position} this {@link Mob} is being moved to.
+ *
+ * @return The Position.
+ */
+ public Position getNext() {
+ return next;
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/impl/package-info.java b/src/org/apollo/game/model/event/impl/package-info.java
new file mode 100644
index 00000000..a6527103
--- /dev/null
+++ b/src/org/apollo/game/model/event/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains Event implementations.
+ */
+package org.apollo.game.model.event.impl;
\ No newline at end of file
diff --git a/src/org/apollo/game/model/event/package-info.java b/src/org/apollo/game/model/event/package-info.java
new file mode 100644
index 00000000..8167081f
--- /dev/null
+++ b/src/org/apollo/game/model/event/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains event-related classes.
+ */
+package org.apollo.game.model.event;
\ No newline at end of file
diff --git a/src/org/apollo/game/model/inter/InterfaceConstants.java b/src/org/apollo/game/model/inter/InterfaceConstants.java
index d16e6cbb..d0e44330 100644
--- a/src/org/apollo/game/model/inter/InterfaceConstants.java
+++ b/src/org/apollo/game/model/inter/InterfaceConstants.java
@@ -7,11 +7,6 @@ package org.apollo.game.model.inter;
*/
public class InterfaceConstants {
- /**
- * The avatar design interface id.
- */
- public static final int AVATAR_DESIGN = 3559;
-
/**
* The default inventory tab ids.
*/
diff --git a/src/org/apollo/game/model/inter/InterfaceSet.java b/src/org/apollo/game/model/inter/InterfaceSet.java
index 72835dac..3e77387e 100644
--- a/src/org/apollo/game/model/inter/InterfaceSet.java
+++ b/src/org/apollo/game/model/inter/InterfaceSet.java
@@ -7,10 +7,16 @@ import java.util.Optional;
import org.apollo.game.message.impl.CloseInterfaceMessage;
import org.apollo.game.message.impl.EnterAmountMessage;
import org.apollo.game.message.impl.OpenDialogueInterfaceMessage;
+import org.apollo.game.message.impl.OpenDialogueOverlayMessage;
import org.apollo.game.message.impl.OpenInterfaceMessage;
import org.apollo.game.message.impl.OpenInterfaceSidebarMessage;
+import org.apollo.game.message.impl.OpenOverlayMessage;
+import org.apollo.game.message.impl.OpenSidebarMessage;
+import org.apollo.game.model.World;
import org.apollo.game.model.entity.Player;
+import org.apollo.game.model.event.impl.CloseInterfacesEvent;
import org.apollo.game.model.inter.dialogue.DialogueListener;
+import org.apollo.game.model.inv.InventoryListener;
/**
* Represents the set of interfaces the player has open.
@@ -83,8 +89,11 @@ public final class InterfaceSet {
* Closes the current open interface(s).
*/
public void close() {
- closeAndNotify();
- player.send(new CloseInterfaceMessage());
+ CloseInterfacesEvent event = new CloseInterfacesEvent(player);
+ if (World.getWorld().submit(event)) {
+ closeAndNotify();
+ player.send(new CloseInterfaceMessage());
+ }
}
/**
@@ -136,10 +145,10 @@ public final class InterfaceSet {
}
/**
- * Opens a chat box dialogue.
+ * Opens a dialogue interface.
*
- * @param listener The listener for the dialogue.
- * @param dialogueId The dialogue's id.
+ * @param listener The {@link DialogueListener}.
+ * @param dialogueId The dialogue id.
*/
public void openDialogue(DialogueListener listener, int dialogueId) {
closeAndNotify();
@@ -152,14 +161,39 @@ public final class InterfaceSet {
}
/**
- * Opens a chat box dialogue.
+ * Opens a dialogue.
*
- * @param dialogueId The dialogue's id.
+ * @param dialogueId The dialogue id.
*/
public void openDialogue(int dialogueId) {
openDialogue(null, dialogueId);
}
+ /**
+ * Opens a dialogue overlay interface.
+ *
+ * @param listener The {@link DialogueListener}.
+ * @param dialogueId The dialogue id.
+ */
+ public void openDialogueOverlay(DialogueListener listener, int dialogueId) {
+ closeAndNotify();
+
+ this.dialogueListener = Optional.ofNullable(listener);
+ this.listener = Optional.ofNullable(listener);
+
+ interfaces.put(InterfaceType.DIALOGUE, dialogueId);
+ player.send(new OpenDialogueOverlayMessage(dialogueId));
+ }
+
+ /**
+ * Opens a dialogue overlay.
+ *
+ * @param dialogueId The dialogue id.
+ */
+ public void openDialogueOverlay(int dialogueId) {
+ openDialogueOverlay(null, dialogueId);
+ }
+
/**
* Opens the enter amount dialogue.
*
@@ -170,6 +204,42 @@ public final class InterfaceSet {
player.send(new EnterAmountMessage());
}
+ /**
+ * Opens an overlay interface.
+ *
+ * @param overlay The overlay id.
+ */
+ public void openOverlay(int overlay) {
+ interfaces.put(InterfaceType.OVERLAY, overlay);
+ player.send(new OpenOverlayMessage(overlay));
+ }
+
+ /**
+ * Opens an sidebar interface.
+ *
+ * @param sidebar The sidebar id.
+ */
+ public void openSidebar(int sidebar) {
+ closeAndNotify();
+ interfaces.put(InterfaceType.SIDEBAR, sidebar);
+
+ player.send(new OpenSidebarMessage(sidebar));
+ }
+
+ /**
+ * Opens an sidebar interface with the specified {@link InventoryListener}.
+ *
+ * @param listener The listener.
+ * @param sidebar The sidebar id.
+ */
+ public void openSidebar(InterfaceListener listener, int sidebar) {
+ closeAndNotify();
+ this.listener = Optional.ofNullable(listener);
+ interfaces.put(InterfaceType.SIDEBAR, sidebar);
+
+ player.send(new OpenSidebarMessage(sidebar));
+ }
+
/**
* Opens a window.
*
diff --git a/src/org/apollo/game/model/inter/bank/BankUtils.java b/src/org/apollo/game/model/inter/bank/BankUtils.java
index faa81818..75cffec8 100644
--- a/src/org/apollo/game/model/inter/bank/BankUtils.java
+++ b/src/org/apollo/game/model/inter/bank/BankUtils.java
@@ -3,10 +3,7 @@ package org.apollo.game.model.inter.bank;
import org.apollo.game.model.Item;
import org.apollo.game.model.def.ItemDefinition;
import org.apollo.game.model.entity.Player;
-import org.apollo.game.model.inter.InterfaceListener;
import org.apollo.game.model.inv.Inventory;
-import org.apollo.game.model.inv.InventoryListener;
-import org.apollo.game.model.inv.SynchronizationInventoryListener;
/**
* Contains bank-related utility methods.
@@ -65,18 +62,8 @@ public final class BankUtils {
* @param player The player.
*/
public static void openBank(Player player) {
- InventoryListener invListener = new SynchronizationInventoryListener(player, BankConstants.SIDEBAR_INVENTORY_ID);
- InventoryListener bankListener = new SynchronizationInventoryListener(player, BankConstants.BANK_INVENTORY_ID);
-
- player.getInventory().addListener(invListener);
- player.getBank().addListener(bankListener);
-
- player.getInventory().forceRefresh();
- player.getBank().forceRefresh();
-
- InterfaceListener interListener = new BankInterfaceListener(player, invListener, bankListener);
-
- player.getInterfaceSet().openWindowWithSidebar(interListener, BankConstants.BANK_WINDOW_ID, BankConstants.SIDEBAR_ID);
+ // Required for access within plugin Actions.
+ player.openBank();
}
/**
diff --git a/src/org/apollo/game/sync/block/ChatBlock.java b/src/org/apollo/game/sync/block/ChatBlock.java
index de3eb001..14bbba81 100644
--- a/src/org/apollo/game/sync/block/ChatBlock.java
+++ b/src/org/apollo/game/sync/block/ChatBlock.java
@@ -1,7 +1,7 @@
package org.apollo.game.sync.block;
import org.apollo.game.message.impl.ChatMessage;
-import org.apollo.game.model.setting.PrivilegeLevel;
+import org.apollo.game.model.entity.setting.PrivilegeLevel;
/**
* The chat {@link SynchronizationBlock}. Only players can utilise this block.
diff --git a/src/org/apollo/io/player/impl/BinaryPlayerLoader.java b/src/org/apollo/io/player/impl/BinaryPlayerLoader.java
index ada3d857..19d77500 100644
--- a/src/org/apollo/io/player/impl/BinaryPlayerLoader.java
+++ b/src/org/apollo/io/player/impl/BinaryPlayerLoader.java
@@ -20,11 +20,12 @@ import org.apollo.game.model.entity.attr.AttributeType;
import org.apollo.game.model.entity.attr.BooleanAttribute;
import org.apollo.game.model.entity.attr.NumericalAttribute;
import org.apollo.game.model.entity.attr.StringAttribute;
+import org.apollo.game.model.entity.setting.Gender;
+import org.apollo.game.model.entity.setting.MembershipStatus;
+import org.apollo.game.model.entity.setting.PrivacyState;
+import org.apollo.game.model.entity.setting.PrivilegeLevel;
+import org.apollo.game.model.entity.setting.ScreenBrightness;
import org.apollo.game.model.inv.Inventory;
-import org.apollo.game.model.setting.Gender;
-import org.apollo.game.model.setting.PrivacyState;
-import org.apollo.game.model.setting.PrivilegeLevel;
-import org.apollo.game.model.setting.ScreenBrightness;
import org.apollo.io.player.PlayerLoader;
import org.apollo.io.player.PlayerLoaderResponse;
import org.apollo.net.codec.login.LoginConstants;
@@ -57,35 +58,28 @@ public final class BinaryPlayerLoader implements PlayerLoader {
}
try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
- // read credentials and privileges
String name = StreamUtil.readString(in);
- String pass = StreamUtil.readString(in);
+ String password = StreamUtil.readString(in);
- if (!name.equalsIgnoreCase(credentials.getUsername()) || !SCryptUtil.check(credentials.getPassword(), pass)) {
+ if (!name.equalsIgnoreCase(credentials.getUsername()) || !SCryptUtil.check(credentials.getPassword(), password)) {
return new PlayerLoaderResponse(LoginConstants.STATUS_INVALID_CREDENTIALS);
}
- // set the credentials password to the scrypted one
- credentials.setPassword(pass);
+ credentials.setPassword(password); // Update password to the hashed one.
PrivilegeLevel privilegeLevel = PrivilegeLevel.valueOf(in.readByte());
- boolean members = in.readBoolean();
+ MembershipStatus members = MembershipStatus.valueOf(in.readByte());
- // read settings
PrivacyState chatPrivacy = PrivacyState.valueOf(in.readByte(), true);
PrivacyState friendPrivacy = PrivacyState.valueOf(in.readByte(), false);
PrivacyState tradePrivacy = PrivacyState.valueOf(in.readByte(), false);
int runEnergy = in.readByte();
ScreenBrightness brightness = ScreenBrightness.valueOf(in.readByte());
- // read position
int x = in.readUnsignedShort();
int y = in.readUnsignedShort();
int height = in.readUnsignedByte();
- // read appearance
- boolean designed = in.readBoolean();
-
int genderIntValue = in.readUnsignedByte();
Gender gender = genderIntValue == Gender.MALE.toInteger() ? Gender.MALE : Gender.FEMALE;
int[] style = new int[7];
@@ -106,15 +100,12 @@ public final class BinaryPlayerLoader implements PlayerLoader {
player.setRunEnergy(runEnergy);
player.setScreenBrightness(brightness);
- player.setNew(designed);
player.setAppearance(new Appearance(gender, style, colors));
- // read inventories
readInventory(in, player.getInventory());
readInventory(in, player.getEquipment());
readInventory(in, player.getBank());
- // read skills
int size = in.readUnsignedByte();
SkillSet skills = player.getSkillSet();
skills.stopFiringEvents();
@@ -138,7 +129,7 @@ public final class BinaryPlayerLoader implements PlayerLoader {
int ignoreCount = in.readByte();
List ignores = new ArrayList<>(ignoreCount);
- for (int i = 0; i < ignoreCount; i++) {
+ for (int times = 0; times < ignoreCount; times++) {
ignores.add(NameUtil.decodeBase37(in.readLong()));
}
player.setIgnoredUsernames(ignores);
@@ -160,27 +151,28 @@ public final class BinaryPlayerLoader implements PlayerLoader {
private static Map> readAttributes(DataInputStream in) throws IOException {
int count = in.readInt();
Map> attributes = new HashMap<>(count);
- Attribute> attribute;
- for (int i = 0; i < count; i++) {
+ for (int times = 0; times < count; times++) {
String name = StreamUtil.readString(in);
AttributeType type = AttributeType.valueOf(in.read());
+ Attribute> attribute;
+
switch (type) {
- case BOOLEAN:
- attribute = new BooleanAttribute(in.read() == 1);
- break;
- case DOUBLE:
- attribute = new NumericalAttribute(in.readDouble());
- break;
- case LONG:
- attribute = new NumericalAttribute(in.readLong());
- break;
- case STRING:
- case SYMBOL:
- attribute = new StringAttribute(StreamUtil.readString(in), type == AttributeType.SYMBOL);
- break;
- default:
- throw new IllegalArgumentException("Undefined attribute type: " + type + ".");
+ case BOOLEAN:
+ attribute = new BooleanAttribute(in.read() == 1);
+ break;
+ case DOUBLE:
+ attribute = new NumericalAttribute(in.readDouble());
+ break;
+ case LONG:
+ attribute = new NumericalAttribute(in.readLong());
+ break;
+ case STRING:
+ case SYMBOL:
+ attribute = new StringAttribute(StreamUtil.readString(in), type == AttributeType.SYMBOL);
+ break;
+ default:
+ throw new IllegalArgumentException("Undefined attribute type: " + type + ".");
}
attributes.put(name, attribute);
}
diff --git a/src/org/apollo/io/player/impl/BinaryPlayerSaver.java b/src/org/apollo/io/player/impl/BinaryPlayerSaver.java
index 082731cf..5fca15a2 100644
--- a/src/org/apollo/io/player/impl/BinaryPlayerSaver.java
+++ b/src/org/apollo/io/player/impl/BinaryPlayerSaver.java
@@ -17,7 +17,6 @@ import org.apollo.game.model.entity.SkillSet;
import org.apollo.game.model.entity.attr.Attribute;
import org.apollo.game.model.entity.attr.AttributeMap;
import org.apollo.game.model.entity.attr.AttributePersistence;
-import org.apollo.game.model.entity.attr.AttributeType;
import org.apollo.game.model.inv.Inventory;
import org.apollo.io.player.PlayerSaver;
import org.apollo.util.NameUtil;
@@ -35,27 +34,22 @@ public final class BinaryPlayerSaver implements PlayerSaver {
File file = BinaryPlayerUtil.getFile(player.getUsername());
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
- // write credentials and privileges
StreamUtil.writeString(out, player.getUsername());
StreamUtil.writeString(out, player.getCredentials().getPassword());
out.writeByte(player.getPrivilegeLevel().toInteger());
- out.writeBoolean(player.isMembers());
+ out.writeByte(player.getMembershipStatus().getValue());
- // write settings
out.writeByte(player.getChatPrivacy().toInteger(true));
out.writeByte(player.getFriendPrivacy().toInteger(false));
out.writeByte(player.getTradePrivacy().toInteger(false));
out.writeByte(player.getRunEnergy());
out.writeByte(player.getScreenBrightness().toInteger());
- // write position
Position position = player.getPosition();
out.writeShort(position.getX());
out.writeShort(position.getY());
out.writeByte(position.getHeight());
- // write appearance
- out.writeBoolean(player.isNew());
Appearance appearance = player.getAppearance();
out.writeByte(appearance.getGender().toInteger());
int[] style = appearance.getStyle();
@@ -66,14 +60,11 @@ public final class BinaryPlayerSaver implements PlayerSaver {
for (int color : colors) {
out.writeByte(color);
}
- out.flush();
- // write inventories
writeInventory(out, player.getInventory());
writeInventory(out, player.getEquipment());
writeInventory(out, player.getBank());
- // write skills
SkillSet skills = player.getSkillSet();
out.writeByte(skills.size());
for (int id = 0; id < skills.size(); id++) {
@@ -95,47 +86,20 @@ public final class BinaryPlayerSaver implements PlayerSaver {
}
Set>> attributes = player.getAttributes().entrySet();
- attributes.removeIf(e -> AttributeMap.getDefinition(e.getKey()).getPersistence() != AttributePersistence.SERIALIZED);
+ attributes.removeIf(e -> AttributeMap.getDefinition(e.getKey()).getPersistence() != AttributePersistence.PERSISTENT);
out.writeInt(attributes.size());
for (Entry> entry : attributes) {
String name = entry.getKey();
StreamUtil.writeString(out, name);
- saveAttribute(out, entry.getValue());
+
+ Attribute> attribute = entry.getValue();
+ out.writeByte(attribute.getType().getValue());
+ out.write(attribute.encode());
}
}
}
- /**
- * Writes an {@link Attribute} to the specified output stream.
- *
- * @param out The output stream.
- * @param attribute The attribute.
- * @throws IOException If an I/O error occurs.
- */
- private static void saveAttribute(DataOutputStream out, Attribute> attribute) throws IOException {
- AttributeType type = attribute.getType();
-
- out.writeByte(type.getValue());
- switch (type) {
- case BOOLEAN:
- out.writeByte((Boolean) attribute.getValue() ? 1 : 0);
- break;
- case DOUBLE:
- out.writeDouble((Double) attribute.getValue());
- break;
- case LONG:
- out.writeLong((Long) attribute.getValue());
- break;
- case STRING:
- case SYMBOL:
- StreamUtil.writeString(out, (String) attribute.getValue());
- break;
- default:
- throw new IllegalArgumentException("Undefined attribute type " + type + ".");
- }
- }
-
/**
* Writes an inventory to the specified output stream.
*
diff --git a/src/org/apollo/io/player/impl/BinaryPlayerUtil.java b/src/org/apollo/io/player/impl/BinaryPlayerUtil.java
index 85ce31b5..9c4ca633 100644
--- a/src/org/apollo/io/player/impl/BinaryPlayerUtil.java
+++ b/src/org/apollo/io/player/impl/BinaryPlayerUtil.java
@@ -32,8 +32,8 @@ public final class BinaryPlayerUtil {
* @return The file.
*/
public static File getFile(String username) {
- username = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
- return new File(SAVED_GAMES_DIRECTORY, username + ".dat");
+ String filtered = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
+ return new File(SAVED_GAMES_DIRECTORY, filtered + ".dat");
}
/**
diff --git a/src/org/apollo/io/player/impl/DummyPlayerLoader.java b/src/org/apollo/io/player/impl/DummyPlayerLoader.java
index 0cc48ed4..83b0aedf 100644
--- a/src/org/apollo/io/player/impl/DummyPlayerLoader.java
+++ b/src/org/apollo/io/player/impl/DummyPlayerLoader.java
@@ -2,7 +2,8 @@ package org.apollo.io.player.impl;
import org.apollo.game.model.Position;
import org.apollo.game.model.entity.Player;
-import org.apollo.game.model.setting.PrivilegeLevel;
+import org.apollo.game.model.entity.setting.MembershipStatus;
+import org.apollo.game.model.entity.setting.PrivilegeLevel;
import org.apollo.io.player.PlayerLoader;
import org.apollo.io.player.PlayerLoaderResponse;
import org.apollo.net.codec.login.LoginConstants;
@@ -26,7 +27,7 @@ public final class DummyPlayerLoader implements PlayerLoader {
Player player = new Player(credentials, DEFAULT_POSITION);
player.setPrivilegeLevel(PrivilegeLevel.ADMINISTRATOR);
- player.setMembers(true);
+ player.setMembers(MembershipStatus.PAID);
return new PlayerLoaderResponse(status, player);
}
diff --git a/src/org/apollo/net/ApolloHandler.java b/src/org/apollo/net/ApolloHandler.java
index ad51eb83..ce0adfc9 100644
--- a/src/org/apollo/net/ApolloHandler.java
+++ b/src/org/apollo/net/ApolloHandler.java
@@ -5,6 +5,8 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpRequest;
+import io.netty.util.Attribute;
+import io.netty.util.ReferenceCountUtil;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -65,25 +67,36 @@ public final class ApolloHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object message) {
- if (ctx.attr(NetworkConstants.SESSION_KEY).get() == null) {
+ try {
+ Attribute attribute = ctx.attr(NetworkConstants.SESSION_KEY);
+ Session session = attribute.get();
+
if (message instanceof HttpRequest || message instanceof JagGrabRequest) {
- new UpdateSession(ctx.channel(), serverContext).messageReceived(message);
- } else {
+ session = new UpdateSession(ctx.channel(), serverContext);
+ }
+
+ if (session != null) {
+ session.messageReceived(message);
+ return;
+ }
+
+ // TODO: Perhaps let HandshakeMessage implement Message to remove this explicit check
+ if (message instanceof HandshakeMessage) {
HandshakeMessage handshakeMessage = (HandshakeMessage) message;
switch (handshakeMessage.getServiceId()) {
case HandshakeConstants.SERVICE_GAME:
- ctx.attr(NetworkConstants.SESSION_KEY).set(new LoginSession(ctx, serverContext));
+ attribute.set(new LoginSession(ctx, serverContext));
break;
+
case HandshakeConstants.SERVICE_UPDATE:
- ctx.attr(NetworkConstants.SESSION_KEY).set(new UpdateSession(ctx.channel(), serverContext));
+ attribute.set(new UpdateSession(ctx.channel(), serverContext));
break;
- default:
- throw new IllegalStateException("Invalid service id.");
}
}
- } else {
- ctx.attr(NetworkConstants.SESSION_KEY).get().messageReceived(message);
+
+ } finally {
+ ReferenceCountUtil.release(message);
}
}
diff --git a/src/org/apollo/net/codec/game/GamePacketDecoder.java b/src/org/apollo/net/codec/game/GamePacketDecoder.java
index 5434ce1e..947cd87f 100644
--- a/src/org/apollo/net/codec/game/GamePacketDecoder.java
+++ b/src/org/apollo/net/codec/game/GamePacketDecoder.java
@@ -4,7 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
-import java.io.IOException;
import java.util.List;
import net.burtleburtle.bob.rand.IsaacRandom;
@@ -61,7 +60,7 @@ public final class GamePacketDecoder extends StatefulFrameDecoder out, GameDecoderState state) throws IOException {
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List