From bb233359d8590229a3c2931860b078277ba4d433 Mon Sep 17 00:00:00 2001 From: Matchu Date: Thu, 7 Oct 2010 10:46:23 -0400 Subject: [PATCH] core of pet loading, still needs get image hash, download assets --- Gemfile | 13 ++-- Gemfile.lock | 2 + app/models/item.rb | 89 +++++++++++++++++++++++- app/models/pet.rb | 55 +++++++++++++++ app/models/pet_state.rb | 71 ++++++++++++++++++- app/models/pet_type.rb | 7 +- app/models/swf_asset.rb | 17 +++++ lib/deferred_attributes.rb | 10 +++ lib/rocketamf/remote_gateway.rb | 18 +++++ lib/rocketamf/remote_gateway/request.rb | 74 ++++++++++++++++++++ lib/rocketamf/remote_gateway/service.rb | 16 +++++ test/factories/pet_state.rb | 2 +- vendor/cache/RocketAMF-0.2.1.gem | Bin 0 -> 26624 bytes 13 files changed, 361 insertions(+), 13 deletions(-) create mode 100644 app/models/pet.rb create mode 100644 lib/deferred_attributes.rb create mode 100644 lib/rocketamf/remote_gateway.rb create mode 100644 lib/rocketamf/remote_gateway/request.rb create mode 100644 lib/rocketamf/remote_gateway/service.rb create mode 100644 vendor/cache/RocketAMF-0.2.1.gem diff --git a/Gemfile b/Gemfile index 3578fbdd..ae4640a8 100644 --- a/Gemfile +++ b/Gemfile @@ -1,22 +1,19 @@ source 'http://rubygems.org' gem 'rails', '3.0.0' - 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 'haml', '~> 3.0.18' +gem 'rdiscount', '~> 1.6.5' +gem 'RocketAMF', '~> 0.2.1' +gem 'will_paginate', '~> 3.0.pre2' group :development do gem 'mysql' end group :test do - gem 'rspec-rails', '~> 2.0.0.beta.22' gem 'factory_girl_rails', '~> 1.0' + gem 'rspec-rails', '~> 2.0.0.beta.22' end diff --git a/Gemfile.lock b/Gemfile.lock index b2a42b57..67118966 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,7 @@ GEM remote: http://rubygems.org/ specs: + RocketAMF (0.2.1) abstract (1.0.0) actionmailer (3.0.0) actionpack (= 3.0.0) @@ -92,6 +93,7 @@ PLATFORMS ruby DEPENDENCIES + RocketAMF (~> 0.2.1) compass (~> 0.10.1) factory_girl_rails (~> 1.0) haml (~> 3.0.18) diff --git a/app/models/item.rb b/app/models/item.rb index 34385c20..a96ce859 100644 --- a/app/models/item.rb +++ b/app/models/item.rb @@ -1,8 +1,10 @@ class Item < ActiveRecord::Base - include SwfAssetParent - 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] PaintbrushSetDescription = 'This item is part of a deluxe paint brush set!' @@ -102,6 +104,89 @@ class Item < ActiveRecord::Base } 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 SearchFilterScopes = [] diff --git a/app/models/pet.rb b/app/models/pet.rb new file mode 100644 index 00000000..5ea031cb --- /dev/null +++ b/app/models/pet.rb @@ -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 diff --git a/app/models/pet_state.rb b/app/models/pet_state.rb index 2b6594b0..5f501da7 100644 --- a/app/models/pet_state.rb +++ b/app/models/pet_state.rb @@ -1,4 +1,73 @@ class PetState < ActiveRecord::Base - include SwfAssetParent 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 diff --git a/app/models/pet_type.rb b/app/models/pet_type.rb index 6be2bf3c..88e00b5f 100644 --- a/app/models/pet_type.rb +++ b/app/models/pet_type.rb @@ -3,7 +3,6 @@ class PetType < ActiveRecord::Base BasicHashes = YAML::load_file(Rails.root.join('config', 'basic_type_hashes.yml')) - StandardBodyIds = PetType.select(arel_table[:body_id]). where(arel_table[:color_id].in(Color::BasicIds)). group(arel_table[:species_id]).map(&:body_id) @@ -55,4 +54,10 @@ class PetType < ActiveRecord::Base def image_hash BasicHashes[species.name][color.name] 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 diff --git a/app/models/swf_asset.rb b/app/models/swf_asset.rb index 23316396..d80fce0e 100644 --- a/app/models/swf_asset.rb +++ b/app/models/swf_asset.rb @@ -33,4 +33,21 @@ class SwfAsset < ActiveRecord::Base def zone @zone ||= Zone.find(zone_id) 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 diff --git a/lib/deferred_attributes.rb b/lib/deferred_attributes.rb new file mode 100644 index 00000000..d044d4d3 --- /dev/null +++ b/lib/deferred_attributes.rb @@ -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 diff --git a/lib/rocketamf/remote_gateway.rb b/lib/rocketamf/remote_gateway.rb new file mode 100644 index 00000000..818a9cda --- /dev/null +++ b/lib/rocketamf/remote_gateway.rb @@ -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 diff --git a/lib/rocketamf/remote_gateway/request.rb b/lib/rocketamf/remote_gateway/request.rb new file mode 100644 index 00000000..508a3aa4 --- /dev/null +++ b/lib/rocketamf/remote_gateway/request.rb @@ -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 diff --git a/lib/rocketamf/remote_gateway/service.rb b/lib/rocketamf/remote_gateway/service.rb new file mode 100644 index 00000000..a86e6eba --- /dev/null +++ b/lib/rocketamf/remote_gateway/service.rb @@ -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 diff --git a/test/factories/pet_state.rb b/test/factories/pet_state.rb index 502f7607..299002b4 100644 --- a/test/factories/pet_state.rb +++ b/test/factories/pet_state.rb @@ -1,4 +1,4 @@ Factory.define :pet_state do |ps| ps.pet_type_id 1 - ps.swf_asset_ids '1,2,3' + ps.swf_asset_ids_cache '1,2,3' end diff --git a/vendor/cache/RocketAMF-0.2.1.gem b/vendor/cache/RocketAMF-0.2.1.gem new file mode 100644 index 0000000000000000000000000000000000000000..dfd9c662262348a7adcb89d5bcc730c5b308ce4d GIT binary patch literal 26624 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 Z{=dOk|Mc+>0{