将 Oshima Core 作为 FunGame WebAPI 插件存在

This commit is contained in:
milimoe 2024-10-13 15:14:41 +08:00
parent 52c9962172
commit da2f1fc23c
Signed by: milimoe
GPG Key ID: 05D280912DA6C69E
20 changed files with 1525 additions and 2 deletions

View File

@ -0,0 +1,7 @@
namespace Oshima.Core.Configs
{
public class BlackList
{
public static Dictionary<long, long> Times { get; } = [];
}
}

117
OshimaCore/Configs/Daily.cs Normal file
View File

@ -0,0 +1,117 @@
using Milimoe.FunGame.Core.Api.Utility;
using Oshima.Core.Constant;
namespace Oshima.Core.Configs
{
public class Daily
{
public static bool DailyNews { get; set; } = false;
public static bool ClearDailys { get; set; } = true;
public static Dictionary<long, string> UserDailys { get; } = [];
public static List<string> GreatFortune { get; set; } = [];
public static List<string> ModerateFortune { get; set; } = [];
public static List<string> GoodFortune { get; set; } = [];
public static List<string> MinorFortune { get; set; } = [];
public static List<string> Misfortune { get; set; } = [];
public static List<string> GreatMisfortune { get; set; } = [];
public static List<DailyType> DailyTypes { get; set; } = [];
public static PluginConfig DailyContent { get; set; } = new("rainbot", "daily");
public static PluginConfig Configs { get; set; } = new("rainbot", "userdaliys");
public static void InitDaily()
{
DailyContent.LoadConfig();
if (DailyContent.TryGetValue("GreatFortune", out object? value) && value != null)
{
GreatFortune = (List<string>)value;
}
if (DailyContent.TryGetValue("ModerateFortune", out value) && value != null)
{
ModerateFortune = (List<string>)value;
}
if (DailyContent.TryGetValue("GoodFortune", out value) && value != null)
{
GoodFortune = (List<string>)value;
}
if (DailyContent.TryGetValue("SmallFortune", out value) && value != null)
{
MinorFortune = (List<string>)value;
}
if (DailyContent.TryGetValue("Misfortune", out value) && value != null)
{
Misfortune = (List<string>)value;
}
if (DailyContent.TryGetValue("GreatMisfortune", out value) && value != null)
{
GreatMisfortune = (List<string>)value;
}
DailyTypes.Clear();
if (GreatFortune.Count != 0)
{
DailyTypes.Add(DailyType.GreatFortune);
}
if (ModerateFortune.Count != 0)
{
DailyTypes.Add(DailyType.ModerateFortune);
}
if (GoodFortune.Count != 0)
{
DailyTypes.Add(DailyType.GoodFortune);
}
if (MinorFortune.Count != 0)
{
DailyTypes.Add(DailyType.MinorFortune);
}
if (Misfortune.Count != 0)
{
DailyTypes.Add(DailyType.Misfortune);
}
if (GreatMisfortune.Count != 0)
{
DailyTypes.Add(DailyType.GreatMisfortune);
}
Configs.LoadConfig();
foreach (string str in Configs.Keys)
{
if (long.TryParse(str, out long qq) && Configs.TryGetValue(str, out object? value2) && value2 != null && !UserDailys.ContainsKey(qq))
{
UserDailys.Add(qq, value2.ToString() ?? "");
if (UserDailys[qq] == "") UserDailys.Remove(qq);
}
}
SaveDaily();
}
public static void SaveDaily()
{
lock (Configs)
{
Configs.Clear();
foreach (long qq in UserDailys.Keys)
{
Configs.Add(qq.ToString(), UserDailys[qq]);
}
Configs.SaveConfig();
}
}
public static void ClearDaily()
{
UserDailys.Clear();
SaveDaily();
}
}
}

View File

@ -0,0 +1,52 @@
using Milimoe.FunGame.Core.Api.Utility;
namespace Oshima.Core.Configs
{
public class GeneralSettings
{
public static bool IsRun { get; set; } = true;
public static long BotQQ { get; set; } = -1;
public static long Master { get; set; } = -1;
public static bool IsDebug { get; set; } = false;
public static long BlackTimes { get; set; } = 5;
public static int BlackFrozenTime { get; set; } = 150;
public static PluginConfig Configs { get; set; } = new("rainbot", "config");
public static void LoadSetting()
{
PluginConfig configs = new("rainbot", "config");
configs.LoadConfig();
if (configs.TryGetValue("BotQQ", out object? value) && value != null)
{
BotQQ = (long)value;
}
if (configs.TryGetValue("Master", out value) && value != null)
{
Master = (long)value;
}
if (configs.TryGetValue("BlackTimes", out value) && value != null)
{
BlackTimes = (long)value;
}
if (configs.TryGetValue("BlackFrozenTime", out value) && value != null)
{
BlackFrozenTime = Convert.ToInt32((long)value);
}
}
public static void SaveConfig()
{
Configs.Add("BotQQ", BotQQ);
Configs.Add("Master", Master);
Configs.Add("BlackTimes", BlackTimes);
Configs.Add("BlackFrozenTime", BlackFrozenTime);
Configs.SaveConfig();
}
}
}

View File

@ -0,0 +1,132 @@
using Milimoe.FunGame.Core.Api.Utility;
namespace Oshima.Core.Configs
{
public class Ignore
{
public static HashSet<string> RepeatIgnore { get; set; } = [];
public static List<long> CallBrotherQQIgnore { get; set; } = [];
/// <summary>
/// 这个属性暂时没用到 标记一下
/// </summary>
public static List<long> QQGroupIgnore { get; set; } = [];
public static List<long> RepeatQQIgnore { get; set; } = [];
public static List<long> ReverseAtIgnore { get; set; } = [];
public static PluginConfig Configs { get; set; } = new("rainbot", "ignore");
public static void InitIgnore()
{
PluginConfig configs = new("rainbot", "ignore");
configs.LoadConfig();
if (configs.TryGetValue("RepeatIgnore", out object? value) && value != null)
{
RepeatIgnore = new HashSet<string>((List<string>)value);
}
if (configs.TryGetValue("CallBrotherQQIgnore", out value) && value != null)
{
CallBrotherQQIgnore = new List<long>((List<long>)value);
}
if (configs.TryGetValue("QQGroupIgnore", out value) && value != null)
{
QQGroupIgnore = (List<long>)value;
}
if (configs.TryGetValue("RepeatQQIgnore", out value) && value != null)
{
RepeatQQIgnore = (List<long>)value;
}
if (configs.TryGetValue("ReverseAtIgnore", out value) && value != null)
{
ReverseAtIgnore = (List<long>)value;
}
}
public static void SaveConfig()
{
Configs.Add("RepeatIgnore", RepeatIgnore);
Configs.Add("CallBrotherQQIgnore", CallBrotherQQIgnore);
Configs.Add("QQGroupIgnore", QQGroupIgnore);
Configs.Add("RepeatQQIgnore", RepeatQQIgnore);
Configs.Add("ReverseAtIgnore", ReverseAtIgnore);
Configs.SaveConfig();
}
public static bool AddValue(string part, bool isadd, object value)
{
try
{
string strv = value.ToString() ?? "";
switch (part.ToLower())
{
case "repeatignore":
if (isadd) RepeatIgnore.Add(strv);
else RepeatIgnore.Remove(strv);
break;
case "callbrotherqqignore":
if (long.TryParse(strv, out long lv))
{
if (isadd) CallBrotherQQIgnore.Add(lv);
else CallBrotherQQIgnore.Remove(lv);
}
break;
case "qqgroupignore":
if (long.TryParse(strv, out lv))
{
if (isadd) QQGroupIgnore.Add(lv);
else QQGroupIgnore.Remove(lv);
}
break;
case "repeatqqignore":
if (long.TryParse(strv, out lv))
{
if (isadd) RepeatQQIgnore.Add(lv);
else RepeatQQIgnore.Remove(lv);
}
break;
case "reverseatignore":
if (long.TryParse(strv, out lv))
{
if (isadd) ReverseAtIgnore.Add(lv);
else ReverseAtIgnore.Remove(lv);
}
break;
default:
return false;
}
}
catch
{
return false;
}
return true;
}
public static string ShowList(string group)
{
List<string> list = [];
switch (group.ToLower())
{
case "repeatignore":
list = [.. RepeatIgnore];
break;
case "callbrotherqqignore":
list = CallBrotherQQIgnore.Select(x => x.ToString()).ToList();
break;
case "qqgroupignore":
list = QQGroupIgnore.Select(x => x.ToString()).ToList();
break;
case "repeatqqignore":
list = RepeatQQIgnore.Select(x => x.ToString()).ToList();
break;
case "reverseatignore":
list = ReverseAtIgnore.Select(x => x.ToString()).ToList();
break;
}
return list.Count > 0 ? "列表" + group + "拥有以下成员:" + "\r\n" + string.Join("\r\n", list) : "此列表不存在或没有任何成员。";
}
}
}

View File

@ -0,0 +1,24 @@
using System.Globalization;
using System.Reflection;
namespace Oshima.Core.Configs
{
public class OSMCore
{
public const string version = "v1.0";
public const string version2 = "Beta4";
public static string Info => $"OSM Core {version} {version2}\r\nAuthor: Milimoe\r\nBuilt on {GetBuiltTime(Assembly.GetExecutingAssembly().Location)}\r\nSee: https://github.com/milimoe";
public static string GetBuiltTime(string dll_name)
{
DateTime lastWriteTime = File.GetLastWriteTime(dll_name);
string month = lastWriteTime.ToString("MMM", CultureInfo.InvariantCulture);
int day = lastWriteTime.Day;
string time = lastWriteTime.ToString("HH:mm:ss", CultureInfo.InvariantCulture);
return $"{month}. {day}, {lastWriteTime.Year} {time}";
}
}
}

View File

@ -0,0 +1,36 @@
using Milimoe.FunGame.Core.Api.Utility;
namespace Oshima.Core.Configs
{
public class QQOpenID
{
public static Dictionary<string, long> QQAndOpenID { get; set; } = [];
public static PluginConfig Configs { get; set; } = new("rainbot", "qqopenid");
public static void LoadConfig()
{
Configs.LoadConfig();
foreach (string str in Configs.Keys)
{
if (Configs.TryGetValue(str, out object? value) && value is long qq && qq != 0)
{
QQAndOpenID.TryAdd(str, qq);
}
}
}
public static void SaveConfig()
{
lock (Configs)
{
Configs.Clear();
foreach (string openid in QQAndOpenID.Keys)
{
Configs.Add(openid, QQAndOpenID[openid]);
}
Configs.SaveConfig();
}
}
}
}

244
OshimaCore/Configs/SayNo.cs Normal file
View File

@ -0,0 +1,244 @@
using Milimoe.FunGame.Core.Api.Utility;
namespace Oshima.Core.Configs
{
public class SayNo
{
public static HashSet<string> Trigger { get; set; } = [];
public static HashSet<string> TriggerBeforeNo { get; set; } = [];
public static HashSet<string> IgnoreTriggerAfterNo { get; set; } = [];
public static HashSet<string> IgnoreTriggerBeforeCan { get; set; } = [];
public static HashSet<string> TriggerAfterYes { get; set; } = [];
public static HashSet<string> WillNotSayNo { get; set; } = [];
public static List<string> SayNoWords { get; set; } = [];
public static List<string> SayDontHaveWords { get; set; } = [];
public static List<string> SayNotYesWords { get; set; } = [];
public static List<string> SayDontWords { get; set; } = [];
public static List<string> SayWantWords { get; set; } = [];
public static List<string> SayThinkWords { get; set; } = [];
public static List<string> SaySpecialNoWords { get; set; } = [];
public static PluginConfig Configs { get; set; } = new("rainbot", "sayno");
public static void InitSayNo()
{
PluginConfig configs = new("rainbot", "sayno");
configs.LoadConfig();
foreach (string key in configs.Keys)
{
if (configs.TryGetValue(key, out object? value) && value != null)
{
switch (key)
{
case "Trigger":
Trigger = new HashSet<string>((List<string>)value);
break;
case "TriggerBeforeNo":
TriggerBeforeNo = new HashSet<string>((List<string>)value);
break;
case "IgnoreTriggerAfterNo":
IgnoreTriggerAfterNo = new HashSet<string>((List<string>)value);
break;
case "IgnoreTriggerBeforeCan":
IgnoreTriggerBeforeCan = new HashSet<string>((List<string>)value);
break;
case "TriggerAfterYes":
TriggerAfterYes = new HashSet<string>((List<string>)value);
break;
case "WillNotSayNo":
WillNotSayNo = new HashSet<string>((List<string>)value);
break;
case "SayNoWords":
SayNoWords = (List<string>)value;
break;
case "SayDontHaveWords":
SayDontHaveWords = (List<string>)value;
break;
case "SayNotYesWords":
SayNotYesWords = (List<string>)value;
break;
case "SayDontWords":
SayDontWords = (List<string>)value;
break;
case "SayWantWords":
SayWantWords = (List<string>)value;
break;
case "SayThinkWords":
SayThinkWords = (List<string>)value;
break;
case "SaySpecialNoWords":
SaySpecialNoWords = (List<string>)value;
break;
}
}
}
}
public static void SaveConfig()
{
Configs.Add("Trigger", Trigger);
Configs.Add("TriggerBeforeNo", TriggerBeforeNo);
Configs.Add("IgnoreTriggerAfterNo", IgnoreTriggerAfterNo);
Configs.Add("IgnoreTriggerBeforeCan", IgnoreTriggerBeforeCan);
Configs.Add("TriggerAfterYes", TriggerAfterYes);
Configs.Add("WillNotSayNo", WillNotSayNo);
Configs.Add("SayNoWords", SayNoWords);
Configs.Add("SayDontHaveWords", SayDontHaveWords);
Configs.Add("SayNotYesWords", SayNotYesWords);
Configs.Add("SayDontWords", SayDontWords);
Configs.Add("SayWantWords", SayWantWords);
Configs.Add("SayThinkWords", SayThinkWords);
Configs.Add("SaySpecialNoWords", SaySpecialNoWords);
Configs.SaveConfig();
}
public static bool AddWord(string part, bool isadd, string value)
{
HashSet<string> set = [];
List<string> list = [];
bool islist = false;
switch (part.ToLower())
{
case "trigger":
set = Trigger;
break;
case "triggerbeforeno":
set = TriggerBeforeNo;
break;
case "ignoretriggerafterno":
set = IgnoreTriggerAfterNo;
break;
case "ignoretriggerbeforecan":
set = IgnoreTriggerBeforeCan;
break;
case "triggerafteryes":
set = TriggerAfterYes;
break;
case "willnotsayno":
set = WillNotSayNo;
break;
case "saynowords":
islist = true;
list = SayNoWords;
break;
case "saydonthavewords":
islist = true;
list = SayDontHaveWords;
break;
case "saynotyeswords":
islist = true;
list = SayNotYesWords;
break;
case "saydontwords":
islist = true;
list = SayDontWords;
break;
case "saywantwords":
islist = true;
list = SayWantWords;
break;
case "saythinkwords":
islist = true;
list = SayThinkWords;
break;
case "sayspecialnowords":
islist = true;
list = SaySpecialNoWords;
break;
default:
return false;
}
if (isadd)
{
if (islist) list.Add(value);
else set.Add(value);
}
else
{
if (islist) list.Remove(value);
else set.Remove(value);
}
return true;
}
public static string ShowList(string part)
{
HashSet<string> set = [];
List<string> list = [];
bool islist = false;
switch (part.ToLower())
{
case "trigger":
set = Trigger;
break;
case "triggerbeforeno":
set = TriggerBeforeNo;
break;
case "ignoretriggerafterno":
set = IgnoreTriggerAfterNo;
break;
case "ignoretriggerbeforecan":
set = IgnoreTriggerBeforeCan;
break;
case "triggerafteryes":
set = TriggerAfterYes;
break;
case "willnotsayno":
set = WillNotSayNo;
break;
case "saynowords":
islist = true;
list = SayNoWords;
break;
case "saydonthavewords":
islist = true;
list = SayDontHaveWords;
break;
case "saynotyeswords":
islist = true;
list = SayNotYesWords;
break;
case "saydontwords":
islist = true;
list = SayDontWords;
break;
case "saywantwords":
islist = true;
list = SayWantWords;
break;
case "saythinkwords":
islist = true;
list = SayThinkWords;
break;
case "sayspecialnowords":
islist = true;
list = SaySpecialNoWords;
break;
default:
break;
}
string msg;
if (islist)
{
msg = list.Count > 0 ? "列表" + part + "拥有以下成员:" + "\r\n" + string.Join("\r\n", list) : "此列表不存在或没有任何成员。";
}
else
{
msg = set.Count > 0 ? "列表" + part + "拥有以下成员:" + "\r\n" + string.Join("\r\n", set) : "此列表不存在或没有任何成员。";
}
return msg;
}
}
}

View File

@ -0,0 +1,13 @@
namespace Oshima.Core.Constant
{
public enum DailyType
{
None,
GreatFortune,
ModerateFortune,
GoodFortune,
MinorFortune,
Misfortune,
GreatMisfortune
}
}

View File

@ -0,0 +1,78 @@
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Oshima.Core.Utils;
namespace Oshima.Core.Controllers
{
[ApiController]
[Route("[controller]")]
public class FunGameController(ILogger<UserDailyController> logger) : ControllerBase
{
private readonly ILogger<UserDailyController> _logger = logger;
[HttpGet("test")]
public List<string> GetTest([FromQuery] bool? isweb = null)
{
if (isweb ?? true)
{
return FunGameSimulation.StartGame(false, true);
}
else
{
return FunGameSimulation.StartGame(false, false);
}
}
[HttpGet("stats")]
public string GetStats([FromQuery] int? id = null)
{
if (id != null && id > 0 && id <= FunGameSimulation.Characters.Count)
{
Character character = FunGameSimulation.Characters[Convert.ToInt32(id) - 1];
if (FunGameSimulation.CharacterStatistics.TryGetValue(character, out CharacterStatistics? stats) && stats != null)
{
StringBuilder builder = new();
builder.AppendLine(character.ToString());
builder.AppendLine($"总计造成伤害:{stats.TotalDamage:0.##} / 场均:{stats.AvgDamage:0.##}");
builder.AppendLine($"总计造成物理伤害:{stats.TotalPhysicalDamage:0.##} / 场均:{stats.AvgPhysicalDamage:0.##}");
builder.AppendLine($"总计造成魔法伤害:{stats.TotalMagicDamage:0.##} / 场均:{stats.AvgMagicDamage:0.##}");
builder.AppendLine($"总计造成真实伤害:{stats.TotalRealDamage:0.##} / 场均:{stats.AvgRealDamage:0.##}");
builder.AppendLine($"总计承受伤害:{stats.TotalTakenDamage:0.##} / 场均:{stats.AvgTakenDamage:0.##}");
builder.AppendLine($"总计承受物理伤害:{stats.TotalTakenPhysicalDamage:0.##} / 场均:{stats.AvgTakenPhysicalDamage:0.##}");
builder.AppendLine($"总计承受魔法伤害:{stats.TotalTakenMagicDamage:0.##} / 场均:{stats.AvgTakenMagicDamage:0.##}");
builder.AppendLine($"总计承受真实伤害:{stats.TotalTakenRealDamage:0.##} / 场均:{stats.AvgTakenRealDamage:0.##}");
builder.AppendLine($"总计存活回合数:{stats.LiveRound} / 场均:{stats.AvgLiveRound}");
builder.AppendLine($"总计行动回合数:{stats.ActionTurn} / 场均:{stats.AvgActionTurn}");
builder.AppendLine($"总计存活时长:{stats.LiveTime:0.##} / 场均:{stats.AvgLiveTime:0.##}");
builder.AppendLine($"总计赚取金钱:{stats.TotalEarnedMoney} / 场均:{stats.AvgEarnedMoney}");
builder.AppendLine($"每回合伤害:{stats.DamagePerRound:0.##}");
builder.AppendLine($"每行动回合伤害:{stats.DamagePerTurn:0.##}");
builder.AppendLine($"每秒伤害:{stats.DamagePerSecond:0.##}");
builder.AppendLine($"总计击杀数:{stats.Kills}" + (stats.Plays != 0 ? $" / 场均:{(double)stats.Kills / stats.Plays:0.##}" : ""));
builder.AppendLine($"总计死亡数:{stats.Deaths}" + (stats.Plays != 0 ? $" / 场均:{(double)stats.Deaths / stats.Plays:0.##}" : ""));
builder.AppendLine($"总计助攻数:{stats.Assists}" + (stats.Plays != 0 ? $" / 场均:{(double)stats.Assists / stats.Plays:0.##}" : ""));
builder.AppendLine($"总计参赛数:{stats.Plays}");
builder.AppendLine($"总计冠军数:{stats.Wins}");
builder.AppendLine($"总计前三数:{stats.Top3s}");
builder.AppendLine($"总计败场数:{stats.Loses}");
builder.AppendLine($"胜率:{stats.Winrates * 100:0.##}%");
builder.AppendLine($"前三率:{stats.Top3rates * 100:0.##}%");
builder.AppendLine($"上次排名:{stats.LastRank} / 场均名次:{stats.AvgRank}");
return NetworkUtility.JsonSerialize(builder.ToString());
}
}
return NetworkUtility.JsonSerialize("");
}
[HttpPost("post")]
public string PostName([FromBody] string name)
{
return NetworkUtility.JsonSerialize($"Your Name received successfully: {name}.");
}
}
}

View File

@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Milimoe.FunGame.Core.Api.Utility;
using Oshima.Core.Configs;
using Oshima.Core.Models;
namespace Oshima.Core.Controllers
{
[ApiController]
[Route("[controller]")]
public class QQController(ILogger<QQController> logger) : ControllerBase
{
private readonly ILogger<QQController> _logger = logger;
[HttpPost("bind")]
public string Post([FromBody] BindQQ b)
{
if (QQOpenID.QQAndOpenID.Values.Any(qq => qq == b.QQ))
{
return NetworkUtility.JsonSerialize($"此QQ已被其他人绑定如果你是此QQ的主人请联系客服处理。");
}
if (QQOpenID.QQAndOpenID.TryGetValue(b.Openid, out long bindqq) && bindqq != 0)
{
return NetworkUtility.JsonSerialize($"你已经绑定过:{bindqq},如绑定错误请联系客服处理。");
}
if (QQOpenID.QQAndOpenID.TryAdd(b.Openid, b.QQ))
{
QQOpenID.SaveConfig();
}
else
{
return NetworkUtility.JsonSerialize($"绑定失败,请稍后再试!如持续绑定失败请联系客服处理。");
}
return NetworkUtility.JsonSerialize("绑定成功!");
}
}
}

View File

@ -0,0 +1,61 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Milimoe.FunGame.Core.Api.Utility;
using Oshima.Core.Configs;
using Oshima.Core.Models;
using Oshima.Core.Utils;
namespace Oshima.Core.Controllers
{
[ApiController]
[Route("[controller]")]
public class UserDailyController(ILogger<UserDailyController> logger) : ControllerBase
{
private readonly ILogger<UserDailyController> _logger = logger;
[HttpGet("{user_id}", Name = "GetUserDaily")]
public UserDaily Get(long user_id)
{
return UserDailyUtil.GetUserDaily(user_id);
}
[HttpGet("v/{user_id}", Name = "ViewUserDaily")]
public UserDaily View(long user_id)
{
return UserDailyUtil.ViewUserDaily(user_id);
}
[HttpGet("open/{open_id}", Name = "GetOpenUserDaily")]
public UserDaily Open(string open_id)
{
if (QQOpenID.QQAndOpenID.TryGetValue(open_id, out long qq) && qq != 0)
{
return UserDailyUtil.GetUserDaily(qq);
}
return new(0, 0, "你似乎没有绑定QQ呢请先发送【绑定+QQ号】绑定123456789再使用哦");
}
[HttpGet("r/{user_id}", Name = "RemoveUserDaily")]
public string Remove(long user_id)
{
return UserDailyUtil.RemoveDaily(user_id);
}
[HttpGet("img/{type}", Name = "GetTypeImage")]
public string GetTypeImage(int type)
{
string img = $"{Request.Scheme}://{Request.Host}{Request.PathBase}/images/zi/";
img += type switch
{
1 => "dj" + (Random.Shared.Next(3) + 1) + ".png",
2 => "zj" + (Random.Shared.Next(2) + 1) + ".png",
3 => "j" + (Random.Shared.Next(4) + 1) + ".png",
4 => "mj" + (Random.Shared.Next(2) + 1) + ".png",
5 => "x" + (Random.Shared.Next(2) + 1) + ".png",
6 => "dx" + (Random.Shared.Next(2) + 1) + ".png",
_ => ""
};
return NetworkUtility.JsonSerialize(img);
}
}
}

View File

@ -0,0 +1,8 @@
namespace Oshima.Core.Models
{
public class BindQQ(string openid, long qq)
{
public string Openid { get; set; } = openid;
public long QQ { get; set; } = qq;
}
}

View File

@ -0,0 +1,9 @@
namespace Oshima.Core.Models
{
public class UserDaily(long user_id, long type, string daily)
{
public long user_id { get; set; } = user_id;
public long type { get; set; } = type;
public string daily { get; set; } = daily;
}
}

View File

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Oshima.Core</RootNamespace>
<AssemblyName>OshimaCore</AssemblyName>
<BaseOutputPath>..\bin\</BaseOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;IDE0130;CA1822;IDE1006</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<NoWarn>1701;1702;IDE0130;CA1822;IDE1006</NoWarn>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OshimaModules\OshimaModules.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="FunGame.Core">
<HintPath>..\..\FunGame.Core\bin\Debug\net8.0\FunGame.Core.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,41 @@
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Exception;
using Oshima.FunGame.OshimaModules;
namespace Oshima.Core.WebAPI
{
public class OshimaWebAPI : WebAPIPlugin
{
public override string Name => OshimaGameModuleConstant.WebAPI;
public override string Description => OshimaGameModuleConstant.Description;
public override string Version => OshimaGameModuleConstant.Version;
public override string Author => OshimaGameModuleConstant.Author;
public override void AfterLoad(params object[] objs)
{
base.AfterLoad(objs);
}
protected override bool BeforeLoad(params object[] objs)
{
if (objs.Length > 0 && objs[0] is Dictionary<string, object> delegates)
{
if (delegates.TryGetValue("WriteLine", out object? value) && value is Action<string> a)
{
WriteLine = a;
}
if (delegates.TryGetValue("Error", out value) && value is Action<Exception> e)
{
Error = e;
}
}
return true;
}
public Action<string> WriteLine { get; set; } = new Action<string>(Console.WriteLine);
public Action<Exception> Error { get; set; } = new Action<Exception>(e => Console.WriteLine(e.GetErrorInfo()));
}
}

View File

@ -0,0 +1,12 @@
{
"profiles": {
"WebAPIExtension": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:11108;http://localhost:11109"
}
}
}

View File

@ -0,0 +1,542 @@
using System.Text;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Constant;
using Oshima.FunGame.OshimaModules.Characters;
using Oshima.FunGame.OshimaModules.Items;
using Oshima.FunGame.OshimaModules.Skills;
namespace Oshima.Core.Utils
{
public class FunGameSimulation
{
public FunGameSimulation()
{
InitCharacter();
bool printout = true;
List<string> strs = StartGame(printout);
if (printout == false)
{
foreach (string str in strs)
{
Console.WriteLine(str);
}
}
Console.ReadKey();
}
public static List<Character> Characters { get; } = [];
public static Dictionary<Character, CharacterStatistics> CharacterStatistics { get; } = [];
public static PluginConfig StatsConfig { get; } = new(nameof(FunGameSimulation), nameof(CharacterStatistics));
public static bool IsRuning { get; set; } = false;
public static bool IsWeb { get; set; } = false;
public static bool PrintOut { get; set; } = false;
public static string Msg { get; set; } = "";
public static List<string> StartGame(bool printout, bool isWeb = false)
{
PrintOut = printout;
IsWeb = isWeb;
try
{
if (IsRuning) return ["游戏正在模拟中,请勿重复请求!"];
List<string> result = [];
int deaths = 0;
Msg = "";
IsRuning = true;
// M = 0, W = 7, P1 = 1, P3 = 1
// M = 1, W = 6, P1 = 2, P3 = 0
// M = 2, W = 4, P1 = 0, P3 = 2
// M = 2, W = 5, P1 = 0, P3 = 0
// M = 3, W = 3, P1 = 1, P3 = 1
// M = 4, W = 2, P1 = 2, P3 = 0
// M = 5, W = 0, P1 = 0, P3 = 2
// M = 5, W = 1, P1 = 0, P3 = 0
List<Character> list = new(Characters);
if (list.Count > 11)
{
if (PrintOut) Console.WriteLine();
if (PrintOut) Console.WriteLine("Start!!!");
if (PrintOut) Console.WriteLine();
Character character1 = list[0].Copy();
Character character2 = list[1].Copy();
Character character3 = list[2].Copy();
Character character4 = list[3].Copy();
Character character5 = list[4].Copy();
Character character6 = list[5].Copy();
Character character7 = list[6].Copy();
Character character8 = list[7].Copy();
Character character9 = list[8].Copy();
Character character10 = list[9].Copy();
Character character11 = list[10].Copy();
Character character12 = list[11].Copy();
List<Character> characters = [
character1, character2, character3, character4,
character5, character6, character7, character8,
character9, character10, character11, character12
];
int clevel = 60;
int slevel = 6;
int mlevel = 8;
// 升级和赋能
for (int index = 0; index < characters.Count; index++)
{
Character c = characters[index];
c.Level = clevel;
c.NormalAttack.Level = mlevel;
Skill = new (c)
{
Level = mlevel
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
if (c == character1)
{
Skill META马 = new META马(c)
{
Level = 1
};
c.Skills.Add(META马);
Skill = new (c)
{
Level = mlevel
};
c.Skills.Add();
}
if (c == character2)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character3)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character4)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character5)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character6)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character7)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character8)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character9)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character10)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character11)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
if (c == character12)
{
Skill = new (c)
{
Level = 1
};
c.Skills.Add();
Skill = new (c)
{
Level = slevel
};
c.Skills.Add();
}
}
// 显示角色信息
if (PrintOut) characters.ForEach(c => Console.WriteLine(c.GetInfo()));
// 创建顺序表并排序
ActionQueue actionQueue = new(characters, WriteLine);
if (PrintOut) Console.WriteLine();
// 显示初始顺序表
actionQueue.DisplayQueue();
if (PrintOut) Console.WriteLine();
// 总游戏时长
double totalTime = 0;
// 开始空投
Msg = "";
(actionQueue, totalTime);
if (isWeb) result.Add("=== 空投 ===\r\n" + Msg);
// 总回合数
int i = 1;
while (i < 999)
{
Msg = "";
if (i == 998)
{
WriteLine($"=== 终局审判 ===");
Dictionary<Character, double> = [];
foreach (Character c in characters)
{
.TryAdd(c, Calculation.Round4Digits(c.HP / c.MaxHP));
}
double max = .Values.Max();
Character winner = .Keys.Where(c => [c] == max).First();
WriteLine("[ " + winner + " ] 成为了天选之人!!");
foreach (Character c in characters.Where(c => c != winner && c.HP > 0))
{
WriteLine("[ " + winner + " ] 对 [ " + c + " ] 造成了 99999999999 点真实伤害。");
actionQueue.DeathCalculation(winner, c);
}
actionQueue.EndGameInfo(winner);
result.Add(Msg);
break;
}
// 检查是否有角色可以行动
Character? characterToAct = actionQueue.NextCharacter();
// 处理回合
if (characterToAct != null)
{
WriteLine($"=== Round {i++} ===");
WriteLine("现在是 [ " + characterToAct + " ] 的回合!");
bool isGameEnd = actionQueue.ProcessTurn(characterToAct);
if (isGameEnd)
{
result.Add(Msg);
break;
}
actionQueue.DisplayQueue();
WriteLine("");
}
// 模拟时间流逝
totalTime += actionQueue.TimeLapse();
if (actionQueue.Eliminated.Count > deaths)
{
deaths = actionQueue.Eliminated.Count;
if (!isWeb)
{
string roundMsg = Msg;
string[] strs = roundMsg.Split("==== 角色状态 ====");
if (strs.Length > 0)
{
roundMsg = strs[0];
}
result.Add(roundMsg);
}
else result.Add(Msg);
}
}
if (PrintOut)
{
Console.WriteLine("--- End ---");
Console.WriteLine("总游戏时长:" + Calculation.Round2Digits(totalTime));
Console.WriteLine("");
}
// 赛后统计
WriteLine("=== 伤害排行榜 ===");
int top = isWeb ? 12 : 6;
Msg = $"=== 伤害排行榜 TOP{top} ===\r\n";
int count = 1;
foreach (Character character in actionQueue.CharacterStatistics.OrderByDescending(d => d.Value.TotalDamage).Select(d => d.Key))
{
StringBuilder builder = new();
CharacterStatistics stats = actionQueue.CharacterStatistics[character];
builder.AppendLine($"{count}. [ {character.ToStringWithLevel()} ] {stats.Kills} / {stats.Assists}");
builder.AppendLine($"存活时长:{stats.LiveTime} / 存活回合数:{stats.LiveRound} / 行动回合数:{stats.ActionTurn}");
builder.AppendLine($"总计伤害:{stats.TotalDamage} / 总计物理伤害:{stats.TotalPhysicalDamage} / 总计魔法伤害:{stats.TotalMagicDamage}");
builder.AppendLine($"总承受伤害:{stats.TotalTakenDamage} / 总承受物理伤害:{stats.TotalTakenPhysicalDamage} / 总承受魔法伤害:{stats.TotalTakenMagicDamage}");
builder.Append($"每秒伤害:{stats.DamagePerSecond} / 每回合伤害:{stats.DamagePerTurn}");
if (count++ <= top)
{
WriteLine(builder.ToString());
}
else
{
if (PrintOut) Console.WriteLine(builder.ToString());
}
CharacterStatistics? totalStats = CharacterStatistics.Where(kv => kv.Key.GetName() == character.GetName()).Select(kv => kv.Value).FirstOrDefault();
if (totalStats != null)
{
// 统计此角色的所有数据
totalStats.TotalDamage = Calculation.Round2Digits(totalStats.TotalDamage + stats.TotalDamage);
totalStats.TotalPhysicalDamage = Calculation.Round2Digits(totalStats.TotalPhysicalDamage + stats.TotalPhysicalDamage);
totalStats.TotalMagicDamage = Calculation.Round2Digits(totalStats.TotalMagicDamage + stats.TotalMagicDamage);
totalStats.TotalRealDamage = Calculation.Round2Digits(totalStats.TotalRealDamage + stats.TotalRealDamage);
totalStats.TotalTakenDamage = Calculation.Round2Digits(totalStats.TotalTakenDamage + stats.TotalTakenDamage);
totalStats.TotalTakenPhysicalDamage = Calculation.Round2Digits(totalStats.TotalTakenPhysicalDamage + stats.TotalTakenPhysicalDamage);
totalStats.TotalTakenMagicDamage = Calculation.Round2Digits(totalStats.TotalTakenMagicDamage + stats.TotalTakenMagicDamage);
totalStats.TotalTakenRealDamage = Calculation.Round2Digits(totalStats.TotalTakenRealDamage + stats.TotalTakenRealDamage);
totalStats.LiveRound += stats.LiveRound;
totalStats.ActionTurn += stats.ActionTurn;
totalStats.LiveTime = Calculation.Round2Digits(totalStats.LiveTime + stats.LiveTime);
totalStats.TotalEarnedMoney += stats.TotalEarnedMoney;
totalStats.Kills += stats.Kills;
totalStats.Deaths += stats.Deaths;
totalStats.Assists += stats.Assists;
totalStats.LastRank = stats.LastRank;
double totalRank = totalStats.AvgRank * totalStats.Plays + totalStats.LastRank;
totalStats.Plays += stats.Plays;
if (totalStats.Plays != 0) totalStats.AvgRank = Calculation.Round2Digits(totalRank / totalStats.Plays);
totalStats.Wins += stats.Wins;
totalStats.Top3s += stats.Top3s;
totalStats.Loses += stats.Loses;
if (totalStats.Plays != 0)
{
totalStats.AvgDamage = Calculation.Round2Digits(totalStats.TotalDamage / totalStats.Plays);
totalStats.AvgPhysicalDamage = Calculation.Round2Digits(totalStats.TotalPhysicalDamage / totalStats.Plays);
totalStats.AvgMagicDamage = Calculation.Round2Digits(totalStats.TotalMagicDamage / totalStats.Plays);
totalStats.AvgRealDamage = Calculation.Round2Digits(totalStats.TotalRealDamage / totalStats.Plays);
totalStats.AvgTakenDamage = Calculation.Round2Digits(totalStats.TotalTakenDamage / totalStats.Plays);
totalStats.AvgTakenPhysicalDamage = Calculation.Round2Digits(totalStats.TotalTakenPhysicalDamage / totalStats.Plays);
totalStats.AvgTakenMagicDamage = Calculation.Round2Digits(totalStats.TotalTakenMagicDamage / totalStats.Plays);
totalStats.AvgTakenRealDamage = Calculation.Round2Digits(totalStats.TotalTakenRealDamage / totalStats.Plays);
totalStats.AvgLiveRound = totalStats.LiveRound / totalStats.Plays;
totalStats.AvgActionTurn = totalStats.ActionTurn / totalStats.Plays;
totalStats.AvgLiveTime = Calculation.Round2Digits(totalStats.LiveTime / totalStats.Plays);
totalStats.AvgEarnedMoney = totalStats.TotalEarnedMoney / totalStats.Plays;
totalStats.Winrates = Calculation.Round4Digits(Convert.ToDouble(totalStats.Wins) / Convert.ToDouble(totalStats.Plays));
totalStats.Top3rates = Calculation.Round4Digits(Convert.ToDouble(totalStats.Top3s) / Convert.ToDouble(totalStats.Plays));
}
if (totalStats.LiveRound != 0) totalStats.DamagePerRound = Calculation.Round2Digits(totalStats.TotalDamage / totalStats.LiveRound);
if (totalStats.ActionTurn != 0) totalStats.DamagePerTurn = Calculation.Round2Digits(totalStats.TotalDamage / totalStats.ActionTurn);
if (totalStats.LiveTime != 0) totalStats.DamagePerSecond = Calculation.Round2Digits(totalStats.TotalDamage / totalStats.LiveTime);
}
}
result.Add(Msg);
// 显示每个角色的信息
if (isWeb)
{
for (i = actionQueue.Eliminated.Count - 1; i >= 0; i--)
{
Character character = actionQueue.Eliminated[i];
result.Add($"=== 角色 [ {character} ] ===\r\n{character.GetInfo()}");
}
}
lock (StatsConfig)
{
foreach (Character c in CharacterStatistics.Keys)
{
StatsConfig.Add(c.ToStringWithOutUser(), CharacterStatistics[c]);
}
StatsConfig.SaveConfig();
}
IsRuning = false;
}
return result;
}
catch (Exception ex)
{
IsRuning = false;
Console.WriteLine(ex);
return [ex.ToString()];
}
}
public static void WriteLine(string str)
{
Msg += str + "\r\n";
if (PrintOut) Console.WriteLine(str);
}
public static void (ActionQueue queue, double totalTime)
{
Item[] ;
if (totalTime == 0)
{
WriteLine("社区送温暖了,现在向所有人发放 [ 攻击之爪 +50 ]");
foreach (Character character in queue.Queue)
{
= [new 50()];
foreach (Item item in )
{
queue.Equip(character, EquipItemToSlot.Accessory1, item);
}
}
}
}
public static void InitCharacter()
{
Characters.Add(new OshimaShiya());
Characters.Add(new XinYin());
Characters.Add(new Yang());
Characters.Add(new NanGanYu());
Characters.Add(new NiuNan());
Characters.Add(new DokyoMayor());
Characters.Add(new MagicalGirl());
Characters.Add(new QingXiang());
Characters.Add(new QWQAQW());
Characters.Add(new ColdBlue());
Characters.Add(new dddovo());
Characters.Add(new Quduoduo());
foreach (Character c in Characters)
{
CharacterStatistics.Add(c, new());
}
StatsConfig.LoadConfig();
foreach (Character character in CharacterStatistics.Keys)
{
if (StatsConfig.ContainsKey(character.ToStringWithOutUser()))
{
CharacterStatistics[character] = StatsConfig.Get<CharacterStatistics>(character.ToStringWithOutUser()) ?? CharacterStatistics[character];
}
}
}
}
}

View File

@ -0,0 +1,67 @@
using Milimoe.FunGame.Core.Api.Utility;
using Oshima.Core.Configs;
using Oshima.Core.Constant;
using Oshima.Core.Models;
namespace Oshima.Core.Utils
{
public class UserDailyUtil
{
public static UserDaily GetUserDaily(long user_id)
{
if (Daily.UserDailys.TryGetValue(user_id, out string? value) && value != null && value.Trim() != "")
{
string daily = "你已看过你的今日运势:\r\n" + value;
return new UserDaily(user_id, 0, daily);
}
else
{
if (Daily.GreatFortune.Count == 0 && Daily.ModerateFortune.Count == 0 && Daily.GoodFortune.Count == 0 &&
Daily.MinorFortune.Count == 0 && Daily.Misfortune.Count == 0 && Daily.GreatMisfortune.Count == 0)
{
return new UserDaily(0, 0, "今日运势列表为空,请联系管理员设定。");
}
// 抽个运势
DailyType type = Daily.DailyTypes[Random.Shared.Next(Daily.DailyTypes.Count)];
string text = type switch
{
DailyType.GreatFortune => Daily.GreatFortune[Random.Shared.Next(Daily.GreatFortune.Count)],
DailyType.ModerateFortune => Daily.ModerateFortune[Random.Shared.Next(Daily.ModerateFortune.Count)],
DailyType.GoodFortune => Daily.GoodFortune[Random.Shared.Next(Daily.GoodFortune.Count)],
DailyType.MinorFortune => Daily.MinorFortune[Random.Shared.Next(Daily.MinorFortune.Count)],
DailyType.Misfortune => Daily.Misfortune[Random.Shared.Next(Daily.Misfortune.Count)],
DailyType.GreatMisfortune => Daily.GreatMisfortune[Random.Shared.Next(Daily.GreatMisfortune.Count)],
_ => "",
};
if (text != "")
{
Daily.UserDailys.Add(user_id, text);
string daily = "你的今日运势是:\r\n" + text;
Daily.SaveDaily();
return new UserDaily(user_id, (int)type, daily);
}
return new UserDaily(0, 0, "今日运势列表为空,请联系管理员设定。");
}
}
public static UserDaily ViewUserDaily(long user_id)
{
if (Daily.UserDailys.TryGetValue(user_id, out string? value) && value != null && value.Trim() != "")
{
return new UserDaily(user_id, 0, "TA今天的运势是\r\n" + value);
}
else
{
return new UserDaily(0, 0, "TA还没有抽取今日运势哦快去提醒TA发送【我的运势】抽取运势吧");
}
}
public static string RemoveDaily(long user_id)
{
Daily.UserDailys.Remove(user_id);
Daily.SaveDaily();
return NetworkUtility.JsonSerialize("你的今日运势已重置。");
}
}
}

View File

@ -7,9 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OshimaModules", "OshimaModu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OshimaMaps", "OshimaMaps\OshimaMaps.csproj", "{27847422-7671-4DF8-95EC-975A2E2F5631}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OshimaModes", "OshimaModes\OshimaModes.csproj", "{74922E11-458E-4A91-8D11-BEC9F7758EEB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OshimaModes", "OshimaModes\OshimaModes.csproj", "{74922E11-458E-4A91-8D11-BEC9F7758EEB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OshimaServers", "OshimaServers\OshimaServers.csproj", "{E8C696E0-96E4-45C3-8CB4-A02145179590}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OshimaServers", "OshimaServers\OshimaServers.csproj", "{E8C696E0-96E4-45C3-8CB4-A02145179590}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OshimaCore", "OshimaCore\OshimaCore.csproj", "{978B06DE-2AD8-4DC4-B53A-D744781FDEA1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -33,6 +35,10 @@ Global
{E8C696E0-96E4-45C3-8CB4-A02145179590}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8C696E0-96E4-45C3-8CB4-A02145179590}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8C696E0-96E4-45C3-8CB4-A02145179590}.Release|Any CPU.Build.0 = Release|Any CPU
{978B06DE-2AD8-4DC4-B53A-D744781FDEA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{978B06DE-2AD8-4DC4-B53A-D744781FDEA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{978B06DE-2AD8-4DC4-B53A-D744781FDEA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{978B06DE-2AD8-4DC4-B53A-D744781FDEA1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -5,6 +5,7 @@ namespace Oshima.FunGame.OshimaModules
public class OshimaGameModuleConstant
{
public const string General = "oshima-studios";
public const string WebAPI = "oshima.fungame.webapi";
public const string FastAuto = "oshima.fungame.fastauto";
public const string Character = "oshima.fungame.characters";
public const string Skill = "oshima.fungame.skills";