Skip to content

Instantly share code, notes, and snippets.

@cmingxu
Created September 25, 2014 13:12
Show Gist options
  • Save cmingxu/c032f41055f8e893a9d8 to your computer and use it in GitHub Desktop.
Save cmingxu/c032f41055f8e893a9d8 to your computer and use it in GitHub Desktop.
class DummyItemRealm < ActiveRecord::Base
belongs_to :dummy_item,
:class_name => 'DummyItem',
:foreign_key => 'dummy_item_id'
belongs_to :realm
after_save :create_job
after_save :create_broadcast_job
RANK_FIRST_SIZE = 100
def self.lived(realm)
includes(:dummy_item).where(["dummy_items.enabled = 1 AND dummy_item_realms.realm_id = ? AND ((dummy_item_realms.begin_at <= ? AND dummy_item_realms.end_at >= ?) OR (dummy_item_realms.begin_at = 0 AND dummy_item_realms.end_at = 0))",
realm.id, Time.now.to_i, Time.now.to_i]).order("dummy_item_realms.begin_at DESC, dummy_item_realms.created_at DESC")
end
def self.update_dummy_item_switch(realm_id, status)
Rails.logger.info("~~~~set dummy item switch: realm:#{realm_id}, status:#{status}")
Rails.global_redis.set("dummy_item_#{realm_id}_switch", status)
end
def self.add_dummy_item_points(player, item_name_quantity_hash)
DummyItemRealm.lived(player.realm).each do |event|
next if event.dummy_item.leaderboard_enabled == 0
item_name_quantity_hash.each_pair do |item_name, quantity|
di = event.dummy_item[:dummy_list].find {|d| d[:item_name] == item_name}
player.redis_server.zincrby(RKG.key_for_dummy_item_event_rank(event.id), di[:points] * quantity, player.id) if di
end
end
end
def as_summary_json(player)
{
id: id,
begin_at: begin_at.to_i,
end_at: end_at.to_i,
title: DummyItem.i18n_key(dummy_item.name, "title"),
desc: DummyItem.i18n_key(dummy_item.name, "description"),
icon: dummy_item.icon,
dummy_list: dummy_item.dummy_list,
trade_in_list:dummy_item.trade_in_list
}.merge!({:dummy_item_player_info => self.get_player_info(player)})
end
def forever_event?
return (self.begin_at == 0 && self.end_at == 0)
end
def trade_in(trade_in_id, player)
trade_in_info = dummy_item.trade_in_list.find{|t| t["id"] == trade_in_id.to_i}
return [false, 'dummy-item.errors.trade-in-not-exists'] if player.nil? || trade_in_info.nil?
job = player.chrono_jobs.to_a.find{|j| j.type == "BgJobs::DummyItems" && j.args["event_id"] == self.id && j.args["trade_in_id"] == trade_in_id}
return [false, 'dummy-item.errors.duplicate-trade-in-job'] if job.present?
Doam::RedisLock.with_lock(player.cache_server_redis, "p#{player.id}:event_id#{self.id}:trade_id:#{trade_in_id}") do
# deduct request and free times
suc, err = pay_for_trade_in(trade_in_info, player)
return [false, err] if not suc
if trade_in_info["duration"] > 0
target_item = trade_in_info["target_items"].first || {}
# create job
BgJobs::DummyItems.create!({:realm_id => player.realm_id, :player_id => player.id, :args => {:event_id => self.id, :trade_in_id => trade_in_id, :name => target_item["name"].to_s, :type => target_item["type"].to_s}})
else
self.claim_trade_in(trade_in_info, player)
end
# dummy item trade in logs
LogDb.connection.execute "INSERT INTO dummy_item_trade_in_logs(user_id, player_id, realm_id, event_id, trade_in_id, created_at) VALUES(#{player.user_id}, #{player.id}, #{player.realm_id}, #{self.id}, #{trade_in_id}, '#{Time.now.utc.to_s(:db)}')"
return true, nil
end
end
def pay_for_trade_in(trade_in_info, player)
# check request items
deduct_lua = <<LUA
LUA
lua = <<LUA
LUA
@rubies = 0
trade_in_info["request_items"].each do |req|
@name, @amount = req["name"], req["amount"].to_i
case req["type"]
when "ruby"
return [false, 'dummy-item.errors.request-not-meet'] if player.user.rubies < @amount
@rubies += @amount
next
when "item"
@hash_key = player.items.key
PlayerUsedItem.create(:player => player, :item_name => @name , :quantity => @amount.to_i )
when "resource"
player.capital.touch(@name.to_s.downcase)
@name = "last_" + @name.to_s.downcase
@hash_key = player.capital.resource_hash.key
when "troop"
@hash_key = player.capital.unit_hash.key
else
next
end
lua += <<LUA
if not redis.call("hget", "#{@hash_key}", "#{@name}") or tonumber(redis.call("hget", "#{@hash_key}", "#{@name}")) < #{@amount} then
return {0, 'dummy-item.errors.request-not-meet'}
end
LUA
deduct_lua += <<LUA
redis.call("hincrby", "#{@hash_key}", "#{@name}", 0 - #{@amount})
LUA
end
trade_in_id, total_times = trade_in_info["id"].to_i, trade_in_info["times"].to_i
# check remaining trade_in times if total times config exists
if total_times > 0
dummy_item_times_hash_key = RKG.key_for_dummy_item_times_hash(self.id, player)
lua += <<LUA
if redis.call("hsetnx", "#{dummy_item_times_hash_key}", #{trade_in_id}, #{total_times}) then
redis.call("expireat", "#{dummy_item_times_hash_key}", #{end_at.to_i})
end
if tonumber(redis.call("hget", "#{dummy_item_times_hash_key}", #{trade_in_id})) <= 0 then
return {0, 'dummy-item.errors.remain-times-not-enough'}
end
redis.call("hincrby", "#{dummy_item_times_hash_key}", #{trade_in_id}, -1)
LUA
end
# deduct requests and times
lua += deduct_lua
lua += <<LUA
return {1, nil}
LUA
suc, err = player.redis.eval(lua, [])
return [false, err] if suc == 0
# deduct rubies
if @rubies > 0 && player.user.deduct_rubies(@rubies, "DummyItem")
amount, sr_amount, tr_amount = player.user.deduct_shadow_rubies(@rubies, "DummyItem")
end
player.capital.update_figures
return true, nil
end
def claim_trade_in(trade_in_info, player)
lua = <<LUA
LUA
trade_in_info["target_items"].each do |tar|
@name, @amount = tar["name"].to_s, tar["amount"].to_i
flag = true
case tar["type"]
when "item"
flag = false
@hash_key = player.items.key
# add dummy item points
player.add_item(@name, @amount, "DummyItem-#{self.dummy_item.id}-#{trade_in_info['id']}")
when "resource"
player.capital.touch(@name.to_s.downcase)
@name = "last_" + @name.to_s.downcase
@hash_key = player.capital.resource_hash.key
when "troop"
@hash_key = player.capital.unit_hash.key
else
next
end
lua += <<LUA
if #{flag} == true then
redis.call("hincrby", "#{@hash_key}", "#{@name}", #{@amount})
end
LUA
end
player.redis_server.eval(lua, [])
player.capital.update_figures
end
def get_player_info(player)
event_key = RKG.key_for_dummy_item_event_rank(self.id)
rank, point = nil, 0
if self.dummy_item.leaderboard_enabled == 1
lua = <<LUA
local rank = redis.call("zrevrank", "#{event_key}", #{player.id})
if not rank then
redis.call("zadd", "#{event_key}", 0, #{player.id})
rank = redis.call("zrevrank", "#{event_key}", #{player.id})
end
local point = redis.call("zscore", "#{event_key}", #{player.id})
return {rank+1, point}
LUA
rank, point = player.redis_server.eval(lua, [])
end
remain_hash = player.redis.hgetall(RKG.key_for_dummy_item_times_hash(self.id, player))
if remain_hash.length < dummy_item.trade_in_list.length # initialize remain hash
player.redis.pipelined do
dummy_item.trade_in_list.each do |t|
if remain_hash[t["id"]].nil?
player.redis.hset(RKG.key_for_dummy_item_times_hash(self.id, player), t["id"], t["times"])
remain_hash[t["id"]] = t["times"].to_i
end
end
player.redis.expireat(RKG.key_for_dummy_item_times_hash(self.id, player), self.end_at.to_i)
end
end
{
remain_times: remain_hash,
rank: rank,
point: point
}
end
def create_job
Rails.logger.info("~~~~~#{dummy_item.enabled?}, #{new_record?}, #{updated_at_changed?}")
if dummy_item.enabled? && (new_record? || updated_at_changed?)
Rails.logger.info("~~~~~create job for dummy item realm: #{id}")
Resque.enqueue_at(Time.at(self.end_at).utc, Jobs::DummyItemEvent, {id: id, updated_at: updated_at.utc.to_i, realm_id: realm_id})
end
end
def create_broadcast_job
if dummy_item.enabled? && (new_record? || updated_at_changed?)
Resque.enqueue_at(Time.at(self.begin_at).utc, Jobs::DummyItemEventBroadcast, {realm_id: realm_id})
Resque.enqueue_at(Time.at(self.end_at).utc, Jobs::DummyItemEventBroadcast, {realm_id: realm_id})
end
end
def finish_event
realm = Realm.find_by_id self.realm_id
return if realm.nil?
return if dummy_item.enabled == 0 || dummy_item.leaderboard_enabled == 0
leaderboard = realm.redis_server.zrevrange(RKG.key_for_dummy_item_event_rank(self.id), 0, DummyItemRealm::RANK_FIRST_SIZE - 1)
return if leaderboard.length <= 0
# send rank reward mail
player_list = Player.sw_to_db(realm.id).where("id IN (#{leaderboard.join(',')})").order("field (id, #{leaderboard.join(',')})").includes(:user)
values = ""
locale = (realm.locale || :en).to_sym
self.dummy_item.reward_list.sort{|a, b| a[:min_rank] <=> b[:min_rank]}.each do |reward|
i = reward[:min_rank]
# mail
message = MassMessage.create(
:subject => I18n.t("dummy-item.leaderboard.mail.subject", :locale => locale),
:message => I18n.t("dummy-item.leaderboard.mail.message", :locale => locale),
:details => {:items => reward[:items]},
:realm_id => realm.id
)
while i <= reward[:max_rank] && i <= player_list.length
p = player_list[i-1]
next unless p
# add item
reward[:items].each {|item|
p.add_item(item.first, item.second, "dragonarena")
}
str = ActiveRecord::Base.__send__(:sanitize_sql, ["(?,?,?,?,?,?,?),", 2, Time.now.utc.to_s(:db), p.id, p.realm_id, message.id, "MassMessage", message.subject], '') unless p.nil? || message.nil?
values << str unless p.nil? || message.nil?
i = i + 1
end
end
ReportNotification.connection.execute "INSERT INTO report_notifications(category, created_at, player_id, realm_id, report_id, report_type, summary) VALUES#{values[0..-2]};"
# clean rank set
realm.redis.zremrangebyrank(RKG.key_for_dummy_item_event_rank(self.id), 0, -1)
DummyItemRealm.update_dummy_item_switch(self.realm_id, DummyItemRealm.lived(Realm.find(realm_id)).present?)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment