core of pet loading, still needs get image hash, download assets

This commit is contained in:
Emi Matchu 2010-10-07 10:46:23 -04:00
parent 8b7b40019d
commit bb233359d8
13 changed files with 361 additions and 13 deletions

13
Gemfile
View file

@ -1,22 +1,19 @@
source 'http://rubygems.org' source 'http://rubygems.org'
gem 'rails', '3.0.0' gem 'rails', '3.0.0'
gem 'sqlite3-ruby', :require => 'sqlite3' gem 'sqlite3-ruby', :require => 'sqlite3'
gem 'haml', '~> 3.0.18'
gem 'will_paginate', '~> 3.0.pre2'
gem 'rdiscount', '~> 1.6.5'
gem 'compass', '~> 0.10.1' gem 'compass', '~> 0.10.1'
gem 'haml', '~> 3.0.18'
gem 'rdiscount', '~> 1.6.5'
gem 'RocketAMF', '~> 0.2.1'
gem 'will_paginate', '~> 3.0.pre2'
group :development do group :development do
gem 'mysql' gem 'mysql'
end end
group :test do group :test do
gem 'rspec-rails', '~> 2.0.0.beta.22'
gem 'factory_girl_rails', '~> 1.0' gem 'factory_girl_rails', '~> 1.0'
gem 'rspec-rails', '~> 2.0.0.beta.22'
end end

View file

@ -1,6 +1,7 @@
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
RocketAMF (0.2.1)
abstract (1.0.0) abstract (1.0.0)
actionmailer (3.0.0) actionmailer (3.0.0)
actionpack (= 3.0.0) actionpack (= 3.0.0)
@ -92,6 +93,7 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
RocketAMF (~> 0.2.1)
compass (~> 0.10.1) compass (~> 0.10.1)
factory_girl_rails (~> 1.0) factory_girl_rails (~> 1.0)
haml (~> 3.0.18) haml (~> 3.0.18)

View file

@ -1,8 +1,10 @@
class Item < ActiveRecord::Base class Item < ActiveRecord::Base
include SwfAssetParent
SwfAssetType = 'object' SwfAssetType = 'object'
has_many :parent_swf_asset_relationships, :foreign_key => 'parent_id',
:conditions => {:swf_asset_type => SwfAssetType}
has_many :swf_assets, :through => :parent_swf_asset_relationships
NCRarities = [0, 500] NCRarities = [0, 500]
PaintbrushSetDescription = 'This item is part of a deluxe paint brush set!' PaintbrushSetDescription = 'This item is part of a deluxe paint brush set!'
@ -102,6 +104,89 @@ class Item < ActiveRecord::Base
} }
end end
def before_save
sold_in_mall = false
end
def origin_registry_info=(info)
# bear in mind that numbers from registries are floats
self.species_support_ids = info[:species_support].map(&:to_i)
attribute_names.each do |attribute|
value = info[attribute.to_sym]
if value
value = value.to_i if value.is_a? Float
self[attribute] = value
end
end
end
def self.collection_from_pet_type_and_registries(pet_type, info_registry, asset_registry)
# bear in mind that registries are arrays with many nil elements,
# due to how the parser works
items = {}
item_ids = []
info_registry.each do |info|
if info
item_ids << info[:obj_info_id].to_i
end
end
existing_relationships_by_item_id_and_swf_asset_id = {}
existing_items = Item.find_all_by_id(item_ids, :include => :parent_swf_asset_relationships)
existing_items.each do |item|
items[item.id] = item
relationships_by_swf_asset_id = {}
item.parent_swf_asset_relationships.each do |relationship|
relationships_by_swf_asset_id[relationship.swf_asset_id] = relationship
end
existing_relationships_by_item_id_and_swf_asset_id[item.id] =
relationships_by_swf_asset_id
end
swf_asset_ids = []
asset_registry.each_with_index do |asset_data, index|
swf_asset_ids << index if asset_data
end
existing_swf_assets = SwfAsset.find_all_by_id(swf_asset_ids)
existing_swf_assets_by_id = {}
existing_swf_assets.each do |swf_asset|
existing_swf_assets_by_id[swf_asset.id] = swf_asset
end
relationships_by_item_id = {}
asset_registry.each do |asset_data|
if asset_data
item_id = asset_data[:obj_info_id].to_i
item = items[item_id]
unless item
item = Item.new
item.id = item_id
items[item_id] = item
end
item.origin_registry_info = info_registry[item.id]
swf_asset_id = asset_data[:asset_id].to_i
swf_asset = existing_swf_assets[swf_asset_id]
unless swf_asset
swf_asset = SwfAsset.new
swf_asset.id = swf_asset_id
end
swf_asset.origin_object_data = asset_data
swf_asset.origin_pet_type = pet_type
relationship = existing_relationships_by_item_id_and_swf_asset_id[item.id][swf_asset_id] rescue nil
unless relationship
relationship = ParentSwfAssetRelationship.new
relationship.parent_id = item.id
relationship.swf_asset_type = SwfAssetType
relationship.swf_asset_id = swf_asset.id
end
relationship.swf_asset = swf_asset
relationships_by_item_id[item_id] ||= []
relationships_by_item_id[item_id] << relationship
end
end
relationships_by_item_id.each do |item_id, relationships|
items[item_id].parent_swf_asset_relationships = relationships
end
items.values
end
private private
SearchFilterScopes = [] SearchFilterScopes = []

55
app/models/pet.rb Normal file
View file

@ -0,0 +1,55 @@
class Pet < ActiveRecord::Base
GATEWAY_URL = 'http://www.neopets.com/amfphp/gateway.php'
AMF_SERVICE_NAME = 'CustomPetService'
PET_VIEWER_METHOD = 'getViewerData'
PET_NOT_FOUND_REMOTE_ERROR = 'PHP: Unable to retrieve records from the database.'
belongs_to :pet_type
attr_reader :items
def load!
require 'ostruct'
begin
envelope = Pet.amf_service.fetch(PET_VIEWER_METHOD, name, nil)
rescue RocketAMF::RemoteGateway::AMFError => e
if e.message == PET_NOT_FOUND_REMOTE_ERROR
raise PetNotFound, "Pet #{name.inspect} does not exist"
end
raise
end
contents = OpenStruct.new(envelope.messages[0].data.body)
pet_data = OpenStruct.new(contents.custom_pet)
self.pet_type = PetType.find_or_initialize_by_species_id_and_color_id(
pet_data.species_id.to_i,
pet_data.color_id.to_i
)
self.pet_type.body_id = pet_data.body_id
@pet_state = self.pet_type.add_pet_state_from_biology! pet_data.biology_by_zone
@items = Item.collection_from_pet_type_and_registries(self.pet_type,
contents.object_info_registry, contents.object_asset_registry)
true
end
def self.load(name)
pet = Pet.find_or_initialize_by_name(name)
pet.load!
pet
end
private
def self.amf_service
@amf_service ||= gateway.service AMF_SERVICE_NAME
end
def self.gateway
unless @gateway
require 'rocketamf/remote_gateway'
@gateway = RocketAMF::RemoteGateway.new(GATEWAY_URL)
end
@gateway
end
class PetNotFound < Exception;end
end

View file

@ -1,4 +1,73 @@
class PetState < ActiveRecord::Base class PetState < ActiveRecord::Base
include SwfAssetParent
SwfAssetType = 'biology' SwfAssetType = 'biology'
has_many :parent_swf_asset_relationships, :foreign_key => 'parent_id',
:conditions => {:swf_asset_type => SwfAssetType}
has_many :swf_assets, :through => :parent_swf_asset_relationships
belongs_to :pet_type
alias_method :swf_asset_ids_from_association, :swf_asset_ids
def swf_asset_ids
self['swf_asset_ids']
end
def swf_asset_ids=(ids)
self['swf_asset_ids'] = ids
end
def self.from_pet_type_and_biology_info(pet_type, info)
swf_asset_ids = []
info.each do |asset_info|
if asset_info
swf_asset_ids << asset_info[:part_id].to_i
end
end
swf_asset_ids_str = swf_asset_ids.join(',')
if pet_type.new_record?
pet_state = self.new :swf_asset_ids => swf_asset_ids_str
else
pet_state = self.find_or_initialize_by_pet_type_id_and_swf_asset_ids(
pet_type.id,
swf_asset_ids_str
)
end
existing_swf_assets = SwfAsset.find_all_by_id(swf_asset_ids)
existing_swf_assets_by_id = {}
existing_swf_assets.each do |swf_asset|
existing_swf_assets_by_id[swf_asset.id] = swf_asset
end
existing_relationships_by_swf_asset_id = {}
unless pet_state.new_record?
pet_state.parent_swf_asset_relationships.each do |relationship|
existing_relationships_by_swf_asset_id[relationship.swf_asset_id] = relationship
end
end
pet_state.pet_type = pet_type # save the second case from having to look it up by ID
relationships = []
info.each do |asset_info|
if asset_info
swf_asset_id = asset_info[:part_id].to_i
swf_asset = existing_swf_assets_by_id[swf_asset_id]
unless swf_asset
swf_asset = SwfAsset.new
swf_asset.id = swf_asset_id
end
swf_asset.origin_biology_data = asset_info
swf_asset.origin_pet_type = pet_type
relationship = existing_relationships_by_swf_asset_id[swf_asset_id]
unless relationship
relationship ||= ParentSwfAssetRelationship.new
relationship.parent_id = pet_state.id
relationship.swf_asset_type = SwfAssetType
relationship.swf_asset_id = swf_asset.id
end
relationship.swf_asset = swf_asset
relationships << relationship
end
end
pet_state.parent_swf_asset_relationships = relationships
pet_state
end
end end

View file

@ -3,7 +3,6 @@ class PetType < ActiveRecord::Base
BasicHashes = YAML::load_file(Rails.root.join('config', 'basic_type_hashes.yml')) BasicHashes = YAML::load_file(Rails.root.join('config', 'basic_type_hashes.yml'))
StandardBodyIds = PetType.select(arel_table[:body_id]). StandardBodyIds = PetType.select(arel_table[:body_id]).
where(arel_table[:color_id].in(Color::BasicIds)). where(arel_table[:color_id].in(Color::BasicIds)).
group(arel_table[:species_id]).map(&:body_id) group(arel_table[:species_id]).map(&:body_id)
@ -55,4 +54,10 @@ class PetType < ActiveRecord::Base
def image_hash def image_hash
BasicHashes[species.name][color.name] BasicHashes[species.name][color.name]
end end
def add_pet_state_from_biology!(biology)
pet_state = PetState.from_pet_type_and_biology_info(self, biology)
self.pet_states << pet_state
pet_state
end
end end

View file

@ -33,4 +33,21 @@ class SwfAsset < ActiveRecord::Base
def zone def zone
@zone ||= Zone.find(zone_id) @zone ||= Zone.find(zone_id)
end end
def origin_pet_type=(pet_type)
self.body_id = pet_type.body_id
end
def origin_biology_data=(data)
self.type = 'biology'
self.zone_id = data[:zone_id].to_i
self.url = data[:asset_url]
self.zones_restrict = data[:zones_restrict]
end
def origin_object_data=(data)
self.type = 'object'
self.zone_id = data[:zone_id].to_i
self.url = data[:asset_url]
end
end end

View file

@ -0,0 +1,10 @@
module DeferredAttributes
def attr_deferred(name, &block)
instance_variable_name = "@#{name}"
define_method name do
value = instance_variable_get(instance_variable_name)
return value if value
instance_variable_set(instance_variable_name, self.instance_eval(&block))
end
end
end

View file

@ -0,0 +1,18 @@
require 'net/http'
require 'rocketamf'
require_relative 'remote_gateway/service'
require_relative 'remote_gateway/request'
module RocketAMF
class RemoteGateway
attr_reader :uri
def initialize(url)
@uri = URI.parse url
end
def service(name)
Service.new(self, name)
end
end
end

View file

@ -0,0 +1,74 @@
module RocketAMF
class RemoteGateway
class Request
ERROR_CODE = 'AMFPHP_RUNTIME_ERROR'
def initialize(service, method, *params)
@service = service
@method = method
@params = params
end
def fetch
uri = @service.gateway.uri
data = envelope.serialize
req = Net::HTTP::Post.new(uri.path)
req.body = data
res = Net::HTTP.new(uri.host, uri.port).start { |http| http.request(req) }
case res
when Net::HTTPSuccess, Net::HTTPRedirection
result = RocketAMF::Envelope.new.populate_from_stream(res.body)
first_message_data = result.messages[0].data
if first_message_data.respond_to?(:[]) && first_message_data[:code] == ERROR_CODE
raise AMFError.new(first_message_data)
end
return result
else
error = nil
begin
res.error!
rescue Exception => scoped_error
error = scoped_error
end
raise ConnectionError, "Error connecting to gateway: #{error}"
end
end
private
def envelope
output = Envelope.new
output.messages << wrapper_message
output
end
def wrapper_message
message = Message.new 'null', '/1', [remoting_message]
end
def remoting_message
message = Values::RemotingMessage.new
message.source = @service.name
message.operation = @method
message.body = @params
message
end
end
class ConnectionError < RuntimeError;end
class AMFError < RuntimeError
DATA_KEYS = [:details, :line, :code]
attr_reader *DATA_KEYS
attr_reader :message
def initialize(data)
DATA_KEYS.each do |key|
instance_variable_set "@#{key}", data[key]
end
@message = data[:description]
end
end
end
end

View file

@ -0,0 +1,16 @@
module RocketAMF
class RemoteGateway
class Service
attr_reader :gateway, :name
def initialize(gateway, name)
@gateway = gateway
@name = name
end
def fetch(method, *params)
Request.new(self, method, *params).fetch
end
end
end
end

View file

@ -1,4 +1,4 @@
Factory.define :pet_state do |ps| Factory.define :pet_state do |ps|
ps.pet_type_id 1 ps.pet_type_id 1
ps.swf_asset_ids '1,2,3' ps.swf_asset_ids_cache '1,2,3'
end end

BIN
vendor/cache/RocketAMF-0.2.1.gem vendored Normal file

Binary file not shown.