quests
This commit is contained in:
parent
7d84b1ddd2
commit
6285395adb
|
@ -59,6 +59,69 @@ shards_max = 2
|
||||||
keys_min = 0
|
keys_min = 0
|
||||||
keys_max = 1
|
keys_max = 1
|
||||||
|
|
||||||
|
[quests]
|
||||||
|
enabled = true
|
||||||
|
# in seconds
|
||||||
|
timer = 21600 # 6hr
|
||||||
|
|
||||||
|
# top slot quests
|
||||||
|
[[quests.tier_1]]
|
||||||
|
name = "Orb Finder"
|
||||||
|
required_type = "orb"
|
||||||
|
required_amt = 200
|
||||||
|
reward_diamonds = 5
|
||||||
|
|
||||||
|
[[quests.tier_1]]
|
||||||
|
name = "Star Finder"
|
||||||
|
required_type = "star"
|
||||||
|
required_amt = 5
|
||||||
|
reward_diamonds = 5
|
||||||
|
|
||||||
|
[[quests.tier_1]]
|
||||||
|
name = "Coin Finder"
|
||||||
|
required_type = "coin"
|
||||||
|
required_amt = 2
|
||||||
|
reward_diamonds = 5
|
||||||
|
|
||||||
|
# middle slot quests
|
||||||
|
[[quests.tier_2]]
|
||||||
|
name = "Orb Collector"
|
||||||
|
required_type = "orb"
|
||||||
|
required_amt = 500
|
||||||
|
reward_diamonds = 10
|
||||||
|
|
||||||
|
[[quests.tier_2]]
|
||||||
|
name = "Star Collector"
|
||||||
|
required_type = "star"
|
||||||
|
required_amt = 10
|
||||||
|
reward_diamonds = 10
|
||||||
|
|
||||||
|
[[quests.tier_2]]
|
||||||
|
name = "Coin Collector"
|
||||||
|
required_type = "coin"
|
||||||
|
required_amt = 4
|
||||||
|
reward_diamonds = 10
|
||||||
|
|
||||||
|
# bottom slot quests
|
||||||
|
[[quests.tier_3]]
|
||||||
|
name = "Orb Master"
|
||||||
|
required_type = "orb"
|
||||||
|
required_amt = 1000
|
||||||
|
reward_diamonds = 15
|
||||||
|
|
||||||
|
[[quests.tier_3]]
|
||||||
|
name = "Star Master"
|
||||||
|
required_type = "star"
|
||||||
|
required_amt = 15
|
||||||
|
reward_diamonds = 15
|
||||||
|
|
||||||
|
[[quests.tier_3]]
|
||||||
|
name = "Coin Master"
|
||||||
|
required_type = "coin"
|
||||||
|
required_amt = 6
|
||||||
|
reward_diamonds = 15
|
||||||
|
|
||||||
|
|
||||||
[voting]
|
[voting]
|
||||||
# allow votes to influence a level's difficulty when it
|
# allow votes to influence a level's difficulty when it
|
||||||
# hasn't been set yet. when set to false, all unrated
|
# hasn't been set yet. when set to false, all unrated
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- +migrate up
|
||||||
|
CREATE TABLE quest_timer (
|
||||||
|
account_id INTEGER NOT NULL references accounts(id),
|
||||||
|
next_at TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- +migrate down
|
||||||
|
DROP TABLE quest_timer;
|
|
@ -12,6 +12,7 @@ require "migrate"
|
||||||
require "./enums"
|
require "./enums"
|
||||||
require "./lib/hash"
|
require "./lib/hash"
|
||||||
require "./lib/format"
|
require "./lib/format"
|
||||||
|
require "./lib/xor"
|
||||||
require "./lib/accounts"
|
require "./lib/accounts"
|
||||||
require "./lib/gjp"
|
require "./lib/gjp"
|
||||||
require "./lib/clean"
|
require "./lib/clean"
|
||||||
|
@ -221,7 +222,7 @@ module CrystalGauntlet
|
||||||
else
|
else
|
||||||
if !migrator.latest?
|
if !migrator.latest?
|
||||||
LOG.fatal { "Database hasn\'t been migrated!! Please run #{"crystal-gauntlet migrate".colorize(:white)}" }
|
LOG.fatal { "Database hasn\'t been migrated!! Please run #{"crystal-gauntlet migrate".colorize(:white)}" }
|
||||||
Process.exit(1)
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
server = HTTP::Server.new([
|
server = HTTP::Server.new([
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
require "uri"
|
||||||
|
|
||||||
|
include CrystalGauntlet
|
||||||
|
|
||||||
|
private PAD_STR = "_____" # meaningless but necessary
|
||||||
|
|
||||||
|
private def get_chk_value(chk_str : String)
|
||||||
|
XorCrypt.encrypt_string(Base64.decode_string(chk_str[5..]), XorCrypt::QUESTS_XOR_KEY)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def get_quest_time(account_id : Int32)
|
||||||
|
timer = config_get("quests.timer").as?(Int64) || 0
|
||||||
|
|
||||||
|
begin
|
||||||
|
next_str = DATABASE.query_one("select next_at from quest_timer where account_id = ?", account_id, as: {String})
|
||||||
|
rescue
|
||||||
|
next_at = (Time.utc + timer.seconds).to_s(Format::TIME_FORMAT)
|
||||||
|
DATABASE.exec("insert into quest_timer (account_id, next_at) values (?, ?)", account_id, next_at)
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
next_at = Time.parse(next_str, Format::TIME_FORMAT, Time::Location::UTC)
|
||||||
|
seconds_left = (next_at - Time.utc).total_seconds.to_i
|
||||||
|
if seconds_left <= 0
|
||||||
|
next_at = (Time.utc + timer.seconds).to_s(Format::TIME_FORMAT)
|
||||||
|
DATABASE.exec("update quest_timer set next_at = ? where account_id = ?", next_at, account_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
return seconds_left
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def type_to_i(type : String?)
|
||||||
|
case type
|
||||||
|
when "orb", "orbs"
|
||||||
|
1
|
||||||
|
when "coin", "coins"
|
||||||
|
2
|
||||||
|
when "star", "stars"
|
||||||
|
3
|
||||||
|
else
|
||||||
|
1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def rand_quest(tier : Int32)
|
||||||
|
pool = config_get("quests.tier_#{tier}").as?(Array)
|
||||||
|
if !pool
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
roll = rand(pool.size)
|
||||||
|
quest = pool[roll]?.as?(Hash)
|
||||||
|
|
||||||
|
if !quest
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
[
|
||||||
|
roll,
|
||||||
|
type_to_i(quest["required_type"]?.as?(String)),
|
||||||
|
quest["required_amt"]? || 0,
|
||||||
|
quest["reward_diamonds"]? || 0,
|
||||||
|
quest["name"]? || ""
|
||||||
|
].join(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
CrystalGauntlet.endpoints["/getGJChallenges.php"] = ->(context : HTTP::Server::Context): String {
|
||||||
|
params = URI::Params.parse(context.request.body.not_nil!.gets_to_end)
|
||||||
|
LOG.debug { params.inspect }
|
||||||
|
|
||||||
|
if !config_get("quests.enabled").as?(Bool)
|
||||||
|
LOG.debug { "quests disabled" }
|
||||||
|
return "-1"
|
||||||
|
end
|
||||||
|
|
||||||
|
user_id, account_id = Accounts.auth(params)
|
||||||
|
if !(user_id && account_id)
|
||||||
|
return "-1"
|
||||||
|
end
|
||||||
|
|
||||||
|
time_left = get_quest_time(account_id)
|
||||||
|
|
||||||
|
resp = [
|
||||||
|
PAD_STR,
|
||||||
|
user_id,
|
||||||
|
String.new(get_chk_value(params["chk"])),
|
||||||
|
params["udid"],
|
||||||
|
account_id,
|
||||||
|
time_left,
|
||||||
|
|
||||||
|
rand_quest(1),
|
||||||
|
rand_quest(2),
|
||||||
|
rand_quest(3)
|
||||||
|
].join(":")
|
||||||
|
|
||||||
|
resp_str = Base64.urlsafe_encode(XorCrypt.encrypt_string(resp, XorCrypt::QUESTS_XOR_KEY))
|
||||||
|
|
||||||
|
return PAD_STR + resp_str + "|" + Hashes.gen_solo_3(resp_str)
|
||||||
|
}
|
|
@ -2,14 +2,13 @@ require "uri"
|
||||||
|
|
||||||
include CrystalGauntlet
|
include CrystalGauntlet
|
||||||
|
|
||||||
XOR_KEY = "59182"
|
private PAD_STR = "_____" # meaningless but necessary
|
||||||
PAD_STR = "_____" # meaningless but necessary
|
|
||||||
|
|
||||||
def get_chk_value(chk_str : String)
|
private def get_chk_value(chk_str : String)
|
||||||
XorCrypt.encrypt_string(Base64.decode_string(chk_str[5..]), XOR_KEY)
|
XorCrypt.encrypt_string(Base64.decode_string(chk_str[5..]), XorCrypt::CHEST_XOR_KEY)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_rand(type : String, large = false)
|
private def get_rand(type : String, large = false)
|
||||||
base = "chests.#{large ? "large" : "small"}.#{type}"
|
base = "chests.#{large ? "large" : "small"}.#{type}"
|
||||||
min = config_get("#{base}_min").as?(Int64) || 0
|
min = config_get("#{base}_min").as?(Int64) || 0
|
||||||
max = config_get("#{base}_max").as?(Int64) || 0
|
max = config_get("#{base}_max").as?(Int64) || 0
|
||||||
|
@ -18,9 +17,9 @@ def get_rand(type : String, large = false)
|
||||||
((Random.rand(min.to_f .. (max.to_f + 1)) / increment).floor() * increment).to_i
|
((Random.rand(min.to_f .. (max.to_f + 1)) / increment).floor() * increment).to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
REWARD_TYPES = ["orbs", "diamonds", "shards", "keys"]
|
private REWARD_TYPES = ["orbs", "diamonds", "shards", "keys"]
|
||||||
|
|
||||||
def get_chest(account_id : Int32, large = false) : {Int32?, Int32?}
|
private def get_chest(account_id : Int32, large = false) : {Int32?, Int32?}
|
||||||
begin
|
begin
|
||||||
total, next_at = DATABASE.query_one("select total_opened, next_at from #{large ? "large_chests" : "small_chests"} where account_id = ?", account_id, as: {Int32, String})
|
total, next_at = DATABASE.query_one("select total_opened, next_at from #{large ? "large_chests" : "small_chests"} where account_id = ?", account_id, as: {Int32, String})
|
||||||
rescue
|
rescue
|
||||||
|
@ -30,7 +29,7 @@ def get_chest(account_id : Int32, large = false) : {Int32?, Int32?}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def claim_chest(account_id : Int32, prev_count : Int32, large = false)
|
private def claim_chest(account_id : Int32, prev_count : Int32, large = false)
|
||||||
table = large ? "large_chests" : "small_chests"
|
table = large ? "large_chests" : "small_chests"
|
||||||
timer = config_get("chests.#{large ? "large" : "small"}.timer").as?(Int64) || 0
|
timer = config_get("chests.#{large ? "large" : "small"}.timer").as?(Int64) || 0
|
||||||
next_at = (Time.utc + timer.seconds).to_s(Format::TIME_FORMAT)
|
next_at = (Time.utc + timer.seconds).to_s(Format::TIME_FORMAT)
|
||||||
|
@ -92,7 +91,7 @@ CrystalGauntlet.endpoints["/getGJRewards.php"] = ->(context : HTTP::Server::Cont
|
||||||
params["rewardType"].to_i? || 0
|
params["rewardType"].to_i? || 0
|
||||||
].join(":")
|
].join(":")
|
||||||
|
|
||||||
resp_str = Base64.urlsafe_encode(XorCrypt.encrypt_string(resp, XOR_KEY))
|
resp_str = Base64.urlsafe_encode(XorCrypt.encrypt_string(resp, XorCrypt::CHEST_XOR_KEY))
|
||||||
|
|
||||||
return PAD_STR + resp_str + "|" + Hashes.gen_solo_4(resp_str)
|
return PAD_STR + resp_str + "|" + Hashes.gen_solo_4(resp_str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,23 +91,3 @@ module CrystalGauntlet::Format
|
||||||
hash.map_with_index{ |(i, v)| "#{i}~#{fmt_value(v, false, false, true)}" }.join("~")
|
hash.map_with_index{ |(i, v)| "#{i}~#{fmt_value(v, false, false, true)}" }.join("~")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module CrystalGauntlet::XorCrypt
|
|
||||||
extend self
|
|
||||||
|
|
||||||
def encrypt(x : Bytes, key : Bytes) : Bytes
|
|
||||||
result = Bytes.new(x.size)
|
|
||||||
x.each.with_index() do |chr, index|
|
|
||||||
result[index] = (chr ^ key[index % key.size])
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def encrypt_string(x : String, key : String) : Bytes
|
|
||||||
result = Bytes.new(x.bytesize)
|
|
||||||
x.bytes.each.with_index() do |chr, index|
|
|
||||||
result[index] = (chr ^ key.byte_at(index % key.bytesize))
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
module CrystalGauntlet::XorCrypt
|
||||||
|
extend self
|
||||||
|
|
||||||
|
def encrypt(x : Bytes, key : Bytes) : Bytes
|
||||||
|
result = Bytes.new(x.size)
|
||||||
|
x.each.with_index() do |chr, index|
|
||||||
|
result[index] = (chr ^ key[index % key.size])
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def encrypt_string(x : String, key : String) : Bytes
|
||||||
|
result = Bytes.new(x.bytesize)
|
||||||
|
x.bytes.each.with_index() do |chr, index|
|
||||||
|
result[index] = (chr ^ key.byte_at(index % key.bytesize))
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
QUESTS_XOR_KEY = "19847"
|
||||||
|
CHEST_XOR_KEY = "59182"
|
||||||
|
end
|
Loading…
Reference in New Issue