Compare commits

...

31 Commits

Author SHA1 Message Date
082dad40e9 Discord link modifed 2025-04-01 13:20:12 +00:00
AlessandroCH
75dafc17b2 fix 2025-03-16 20:53:37 +01:00
AlessandroCH
4c62f9f7ef fix 2025-03-16 20:53:37 +01:00
Xannix246
28674c0aed ingame cmd update
Now commands are transfering via base64, and teleport command can increase/decreae position (+ before cords value to increase and -- to decrease)
2025-03-16 01:26:00 +03:00
CloudTronX
209bd16248 added support to decimal coordinate teleportation 2025-03-15 12:22:53 -07:00
Albert
23c6f57bb1
Update README_ru-RU.md 2025-03-15 21:06:04 +03:00
CloudTron
c6b38f06fb
Update README_zh-CN.md 2025-03-15 01:46:51 -07:00
CloudTron
a8863e9b3e
Update README.md 2025-03-15 01:46:14 -07:00
CloudTron
5e4fee92ef
Update README_zh-CN.md 2025-03-15 01:45:53 -07:00
CloudTron
8e1713877a
Update README.md 2025-03-15 01:44:38 -07:00
AlessandroCH
ed0d4252a7 bag saving, grid move and drop 2025-03-11 20:11:05 +01:00
AlessandroCH
2e1b633682 fix 2025-03-11 17:36:21 +01:00
AlessandroCH
4b27010ddd more server status info 2025-03-11 17:29:59 +01:00
AlessandroCH
cf91c97812 server status api 2025-03-11 17:12:12 +01:00
AlessandroCH
6a39a2960e gift to chars work 2025-03-10 21:08:13 +01:00
AlessandroCH
d0e91fdc78 some summary comments 2025-03-09 22:14:29 +01:00
AlessandroCH
c375fb9af7 remove comments 2025-03-09 22:08:29 +01:00
AlessandroCH
b0eb526818 Working on a better resource loader 2025-03-09 22:07:48 +01:00
AlessandroCH
dc3eace03b some summary texts 2025-03-09 18:56:45 +01:00
AlessandroCH
9c1f691fa3 another fix :( 2025-03-09 17:02:38 +01:00
AlessandroCH
cd45f63bd9 fix 2025-03-09 17:01:52 +01:00
AlessandroCH
0c268cb978 fix 2025-03-09 16:56:48 +01:00
AlessandroCH
aedf2a885d some bug fix, new improved inventory manager (but still very bad but atleast you can give some items inside the bag) 2025-03-09 16:28:12 +01:00
Akari
feb6922244
Merge pull request #1 from Smi1eDoge/development
CN Translation and Some Docs Fixes
2025-03-09 07:45:16 +01:00
Smi1eDoge
ae32b92cb6 Markdown Links Fix 2025-03-09 12:35:45 +08:00
Smi1eDoge
6b3d648b5c quick fix 2025-03-09 12:30:11 +08:00
Smi1eDoge
e6122f8817 Add README_zh-CN.md 2025-03-09 12:30:02 +08:00
AlessandroCH
1277d16ec5 fix 2025-03-08 17:12:00 +01:00
AlessandroCH
4ad771a690 fix 2025-03-08 17:10:21 +01:00
AlessandroCH
d7ba6dcb6b new config options 2025-03-08 17:09:30 +01:00
AlessandroCH
5f488b67d9 saving player gender 2025-03-08 15:26:54 +01:00
50 changed files with 1088 additions and 199 deletions

View File

@ -28,15 +28,12 @@ public static class CommandAdd
case "item":
Item item=target.inventoryManager.AddItem(args[1], int.Parse(args[2]));
message = $"Item {args[1]} was added to {target.nickname}";
target.Send(new PacketScItemBagScopeModify(target, item));
break;
case "weapon":
Item wep = target.inventoryManager.AddWeapon(args[1], Convert.ToUInt64(args[2]));
message = $"Weapon {args[1]} was added to {target.nickname}";
target.Send(new PacketScItemBagScopeModify(target, wep));
break;
case "char":
@ -77,7 +74,7 @@ public static class CommandAdd
return;
}
target.inventoryManager.Save();
CommandManager.SendMessage(sender, $"{message}.");
}
catch (Exception err)

View File

@ -61,7 +61,7 @@ namespace Campofinale.Game.Character
}
int updatedItemCount = 0;
foreach (var item in target.inventoryManager.items)
foreach (var item in target.inventoryManager.items.items)
{
if (item.id.StartsWith("wpn_"))
{

View File

@ -19,11 +19,6 @@ namespace Campofinale.Commands.Handlers
return;
}
for (int i=0; i < args.Length; i++)
{
args[i] = Uri.UnescapeDataString(args[i]);
}
target.nickname = string.Join(" ", args);
target.Save();
target.Send(new PacketScSetName(target, target.nickname));

View File

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using static Campofinale.Resource.ResourceManager;
using Campofinale.Packets.Sc;
using MongoDB.Bson;
using System.Globalization;
namespace Campofinale.Commands.Handlers
{
@ -23,20 +24,25 @@ namespace Campofinale.Commands.Handlers
for (int i=0; i < args.Length; i++)
{
args[i] = Uri.UnescapeDataString(args[i]).Replace(".", ",");
args[i] = args[i].Replace(",", ".");
}
float x, y, z;
float[] pos = [target.position.x, target.position.y, target.position.z];
x = args[0] == "~" ? target.position.x : float.Parse(args[0]);
y = args[1] == "~" ? target.position.y : float.Parse(args[1]);
z = args[2] == "~" ? target.position.z : float.Parse(args[2]);
for (int i=0; i < args.Length; i++) {
if(args[i] == "~") continue;
float curPos = pos[i];
pos[i] = float.Parse(args[i].StartsWith("--") ? args[i].Trim('-') : args[i], CultureInfo.InvariantCulture);
if (args[i].StartsWith('+')) pos[i] += curPos;
if (args[i].StartsWith("--")) pos[i] = curPos - pos[i];
}
Vector3f position = new Vector3f(new Vector()
{
X = x,
Y = y,
Z = z
X = pos[0],
Y = pos[1],
Z = pos[2]
});
target.position = position;

View File

@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -14,21 +15,28 @@ namespace Campofinale
public ServerOptions serverOptions = new();
public LogSettings logOptions = new();
}
public struct ServerOptions
public class ServerOptions
{
public int defaultSceneNumId = 98;
public int maxPlayers = 20;
public CharactersOptions defaultCharacters = new();
public ServerOptions()
{
}
public class CharactersOptions
{
public int defaultLevel = 1;
public bool giveAllCharacters = true;
public List<string> characters = new List<string>(); //used if giveAllCharacters is false
public CharactersOptions() { }
}
/* public struct WelcomeMail
{
}*/
}
public struct LogSettings
public class LogSettings
{
public bool packets;
public bool debugPrint=false;
@ -37,7 +45,7 @@ namespace Campofinale
{
}
}
public struct GameserverSettings
public class GameserverSettings
{
public string bindAddress = "127.0.0.1";
public int bindPort = 30000;
@ -47,10 +55,9 @@ namespace Campofinale
{
}
}
public struct DispatchServerSettings
public class DispatchServerSettings
{
public string bindAddress = "127.0.0.1";
public int bindPort = 5000;
public string accessAddress = "127.0.0.1";
public int accessPort = 5000;
@ -60,7 +67,7 @@ namespace Campofinale
}
}
public struct MongoDatabaseSettings
public class MongoDatabaseSettings
{
public string uri = "mongodb://localhost:27017";
public string collection = "Campofinale";

View File

@ -154,7 +154,7 @@
try {
const url = new URL('%dispatchip%/pcSdk/console');
url.searchParams.append('command', command);
url.searchParams.append('command', btoa(unescape(encodeURIComponent(command))));
url.searchParams.append('token', token);
const response = await fetch(url.toString(), {

View File

@ -42,6 +42,8 @@ namespace Campofinale.Database
public List<Scene> scenes = new();
public Dictionary<int, List<int>> bitsets = new();
public PlayerSafeZoneInfo savedSafeZone = new();
public Gender gender = Gender.GenFemale;
public Dictionary<int, Item> bag = new();
}
public class Account
{
@ -141,7 +143,9 @@ namespace Campofinale.Database
noSpawnAnymore = player.noSpawnAnymore,
scenes=player.sceneManager.scenes,
bitsets=player.bitsetManager.bitsets,
savedSafeZone = player.savedSaveZone
savedSafeZone = player.savedSaveZone,
gender=player.gender,
bag=player.inventoryManager.items.bag
};
UpsertPlayerData(data);
}

View File

@ -9,6 +9,7 @@ using MongoDB.Bson.IO;
using MongoDB.Bson;
using System.Reflection;
using static Campofinale.Game.Factory.FactoryNode;
using Campofinale.Game.Inventory;
namespace Campofinale.Database
{
@ -68,6 +69,8 @@ namespace Campofinale.Database
{
BsonSerializer.RegisterSerializer(typeof(Dictionary<int, ulong>), new CustomDictionarySerializer<int, ulong>());
BsonSerializer.RegisterSerializer(typeof(Dictionary<int, List<int>>), new CustomDictionarySerializer<int, List<int>>());
BsonSerializer.RegisterSerializer(typeof(Dictionary<int, Item>), new CustomDictionarySerializer<int, Item>());
RegisterSubclasses<FComponent>();
Logger.Print("Connecting to MongoDB...");
try

View File

@ -149,6 +149,11 @@ namespace Campofinale.Game.Character
guid = GetOwner().random.Next();
this.weaponGuid = GetOwner().inventoryManager.AddWeapon(ResourceManager.charGrowthTable[id].defaultWeaponId, 1).guid;
this.curHp = CalcAttributes()[AttributeType.MaxHp].val;
if (level < 20) breakNode = "";
if (level >= 20 && level <= 40) breakNode = "charBreak20";
if (level > 40 && level <= 60) breakNode = "charBreak40";
if (level > 60 && level <= 70) breakNode = "charBreak60";
if (level > 70) breakNode = "charBreak70";
}
public int GetSkillMaxLevel()
{

View File

@ -16,7 +16,7 @@ namespace Campofinale.Game.Gacha
public Player player;
internal ulong upSeqId;
const double fiftyfifty = 0.45; // 50% (make it less than real 50, because the randomness make win fifty fifty every time
const double fiftyfifty = 0.50;
private static readonly Random random = new Random();
public GachaManager(Player player)
@ -176,7 +176,6 @@ namespace Campofinale.Game.Gacha
RewardIds =
{
$"reward_{transaction.rarity}starChar_weaponCoin",
},
});
@ -220,11 +219,7 @@ namespace Campofinale.Game.Gacha
}
else
{
int index = random.Next(0,items.Count); // Miglior randomizzazione
// index = (int)((1 - Math.Pow(random.NextDouble(), 2)) * (items.Count - 1));
// Se vuoi evitare di prendere spesso i primi 2-3 elementi:
// index = (int)Math.Pow(random.NextDouble(), 1.5) * items.Count;
int index = random.Next(0,items.Count);
if (index > items.Count-1)
{
index = items.Count-1;

View File

@ -0,0 +1,277 @@
using Campofinale.Database;
using Campofinale.Packets.Sc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Game.Inventory
{
public class InventoryList
{
public List<Item> items = new();
public Dictionary<int, Item> bag = new();
public int maxBagSize = 30;
public Player player;
public InventoryList(Player player)
{
this.player = player;
}
public enum FindType
{
Items,
FactoryDepots,
Bag
}
public void UpdateInventoryPacket()
{
}
public void UpdateBagInventoryPacket()
{
player.Send(new PacketScItemBagScopeSync(this.player,Resource.ItemValuableDepotType.Invalid));
}
private void AddToBagAvailableSlot(Item item)
{
for (int i = 0; i < maxBagSize; i++)
{
if (!bag.ContainsKey(i))
{
bag.Add(i, item);
return;
}
}
}
///
///<summary>Add a item directly to the bag if there is enough space or increment current stack value</summary>
///
public bool AddToBag(Item item)
{
Item existOne = Find(i=>i.id == item.id && i.amount < item.GetItemTable().maxStackCount,FindType.Bag);
if (existOne != null)
{
if(existOne.amount+item.amount > item.GetItemTable().maxStackCount)
{
int max = existOne.GetItemTable().maxStackCount;
int toAddInNewSlotAmount = existOne.amount + item.amount - max;
item.amount = toAddInNewSlotAmount;
if (SlotAvailableInBag())
{
existOne.amount = max;
AddToBagAvailableSlot(item);
UpdateBagInventoryPacket();
return true;
}
else
{
return false;
}
}
else
{
existOne.amount += item.amount;
UpdateBagInventoryPacket();
return true;
}
}
else
{
if(bag.Count < maxBagSize)
{
AddToBagAvailableSlot(item);
UpdateBagInventoryPacket();
return true;
}
else
{
return false;
}
}
}
public bool SlotAvailableInBag()
{
bool availableSlot = false;
for (int i = 0; i < maxBagSize; i++)
{
if (!bag.ContainsKey(i))
{
return true;
}
}
return availableSlot;
}
public Item FindInAll(Predicate<Item> match)
{
var item = items.Find(match);
if (item != null)
{
return item;
}
var itemB = bag.Values.ToList().Find(match);
if (itemB != null)
{
return itemB;
}
return null;
}
public Item Find(Predicate<Item> match,FindType findType = FindType.Items)
{
switch (findType)
{
case FindType.Items:
var item = items.Find(match);
if (item != null)
{
return item;
}
break;
case FindType.FactoryDepots:
//TODO
break;
case FindType.Bag:
var itemB = bag.Values.ToList().Find(match);
if (itemB != null)
{
return itemB;
}
break;
}
return null;
}
public List<Item> FindAll(Predicate<Item> match, FindType findType = FindType.Items)
{
switch (findType)
{
case FindType.Items:
return items.FindAll(match);
break;
case FindType.FactoryDepots:
//TODO
break;
case FindType.Bag:
var itemB = bag.Values.ToList().FindAll(match);
if (itemB != null)
{
return itemB;
}
break;
}
return null;
}
///<summary>
///Add an item to the inventory (or increment it's amount if it's not an instance type, else add a new one or add to bag if it's a Factory item
///</summary>
public Item Add(Item item)
{
if (item.StorageSpace() == Resource.ItemStorageSpace.BagAndFactoryDepot)
{
AddToBag(item);
return null;
}
if (item.InstanceType())
{
items.Add(item);
DatabaseManager.db.UpsertItem(item);
return item;
}
else
{
Item exist=Find(i=>i.id==item.id);
if (exist != null)
{
exist.amount += item.amount;
DatabaseManager.db.UpsertItem(exist);
return exist;
}
else
{
items.Add(item);
DatabaseManager.db.UpsertItem(item);
return item;
}
}
}
/// <summary>
/// Get the item amount from all depots
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public int GetItemAmount(string id)
{
int amt = 0;
Item item=Find(i=>i.id==id);
if (item != null)
{
amt += item.amount;
}
List<Item> bagItems = FindAll(i=>i.id==id,FindType.Bag);
foreach (Item bagItem in bagItems)
{
amt += bagItem.amount;
}
return amt;
}
public void Remove(Item item)
{
if (items.Remove(item))
{
this.player.Send(new PacketScItemBagScopeModify(this.player, item));
DatabaseManager.db.DeleteItem(item);
}
else if (RemoveFromBag(item))
{
UpdateBagInventoryPacket();
}
}
private bool RemoveFromBag(Item item)
{
for (int i = 0; i < maxBagSize; i++)
{
Item bagItem = null;
if (bag.ContainsKey(i))
{
bagItem = bag[i];
if (bagItem.guid == item.guid)
{
bag.Remove(i);
return true;
}
}
}
return false;
}
/// <summary>
/// Move item from bag grid to another position
/// </summary>
/// <param name="fromGrid"></param>
/// <param name="toGrid"></param>
public void MoveBagItem(int fromGrid, int toGrid)
{
Item item1 = bag[fromGrid];
Item item2 = null;
if (bag.ContainsKey(toGrid))
{
item2 = bag[toGrid];
}
bag[toGrid] = item1;
if (item2 != null)
{
bag[fromGrid] = item2;
}
else
{
bag.Remove(fromGrid);
}
UpdateBagInventoryPacket();
}
}
}

View File

@ -5,6 +5,7 @@ using Google.Protobuf.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
@ -15,7 +16,7 @@ namespace Campofinale.Game.Inventory
public class InventoryManager
{
public Player owner;
public List<Item> items= new List<Item>();
public InventoryList items;
public int item_diamond_amt
{
@ -36,12 +37,12 @@ namespace Campofinale.Game.Inventory
public Item GetItemById(string id)
{
return items.Find(i => i.id == id);
return items.FindInAll(i => i.id == id);
}
public InventoryManager(Player o) {
owner = o;
items=new(o);
}
public void AddRewards(string rewardTemplateId, Vector3f pos, int sourceType=1)
{
@ -110,46 +111,25 @@ namespace Campofinale.Game.Inventory
}
public void Save()
{
foreach (Item item in items)
foreach (Item item in items.items)
{
DatabaseManager.db.UpsertItem(item);
}
}
public void Load()
{
items = DatabaseManager.db.LoadInventoryItems(owner.roleId);
items.items = DatabaseManager.db.LoadInventoryItems(owner.roleId);
}
public Item AddItem(string id, int amt)
public Item AddItem(string id, int amt, bool notify=false)
{
Item it = new()
Item item = new Item(owner.roleId, id, amt);
Item itemNew = items.Add(item);
if (notify && itemNew != null)
{
id = id,
};
if(!it.InstanceType())
{
Item item = items.Find(i=>i.id == id);
if (item != null)
{
// Logger.Print(id + ": " + amt+" added to existing");
item.amount += amt;
return item;
}
else
{
// Logger.Print(id + ": " + amt + " added to new");
item = new Item(owner.roleId, id, amt);
items.Add(item);
return item;
}
this.owner.Send(new PacketScItemBagScopeModify(this.owner, itemNew));
}
else
{
//Logger.Print(id + ": " + amt + " added to new as instance");
Item item = new Item(owner.roleId, id, amt);
items.Add(item);
return item;
}
return item;
}
public void RemoveItem(Item item,int amt)
{
@ -157,9 +137,46 @@ namespace Campofinale.Game.Inventory
if(item.amount <= 0)
{
items.Remove(item);
DatabaseManager.db.DeleteItem(item);
}
this.owner.Send(new PacketScItemBagScopeModify(this.owner, item));
else
{
this.owner.Send(new PacketScItemBagScopeModify(this.owner, item));
items.UpdateBagInventoryPacket();
}
}
public bool ConsumeItem(string id, int amt)
{
Item item=items.FindInAll(i=>i.id== id);
if (item != null)
{
if(item.amount >= amt)
{
item.amount -= amt;
if(item.amount < 1)
{
items.Remove(item);
}
else
{
this.owner.Send(new PacketScItemBagScopeModify(this.owner, item));
items.UpdateBagInventoryPacket();
}
return true;
}
else
{
int toConsume = amt - item.amount;
item.amount = 0;
items.Remove(item);
return ConsumeItem(id, toConsume);
}
}
else
{
return false;
}
}
public bool ConsumeItems(MapField<string, ulong> costItemId2Count)
{
@ -179,16 +196,8 @@ namespace Campofinale.Game.Inventory
bool found = true;
foreach (ItemInfo item in items)
{
Item i= GetItemById(item.ResId);
if (i != null)
{
if(i.amount < item.ResCount)
{
found = false;
break;
}
}
else
int amount = this.items.GetItemAmount(item.ResId);
if(amount < item.ResCount)
{
found = false;
break;
@ -196,14 +205,7 @@ namespace Campofinale.Game.Inventory
}
foreach (ItemInfo item in items)
{
Item i = GetItemById(item.ResId);
if (i != null)
{
if (i.amount >= item.ResCount)
{
RemoveItem(i,item.ResCount);
}
}
ConsumeItem(item.ResId, item.ResCount);
}
return found;
}
@ -211,13 +213,37 @@ namespace Campofinale.Game.Inventory
public Dictionary<uint, int> GetInventoryChapter(string chapterId)
{
Dictionary<uint, int> dir= new Dictionary<uint, int>();
List<Item> citems = items.FindAll(i=>!i.InstanceType());
/*List<Item> citems = items.FindAll(i=>!i.InstanceType());
foreach (Item item in citems)
{
dir.Add((uint)ResourceManager.strIdNumTable.item_id.dic[item.id], item.amount);
}
}*/
return dir;
}
public void DropItemsBag(CsItemBagAbandonInBag req)
{
if(req.TargetObjectId == 0)
{
foreach (var i in req.GridCut)
{
Item item = items.bag[i.Key];
item.amount -= i.Value;
if(item.amount <= 0)
{
items.bag.Remove(i.Key);
}
owner.sceneManager.CreateDrop(owner.position, new RewardTable.ItemBundle()
{
count=i.Value,
id=item.id,
});
}
}
items.UpdateBagInventoryPacket();
}
}
}

View File

@ -48,6 +48,10 @@ namespace Campofinale.Game.Inventory
this.level = level;
guid = GetOwner().random.Next();
}
public ItemStorageSpace StorageSpace()
{
return ResourceManager.itemTypeTable[GetItemTable().type].storageSpace;
}
public ulong GetDefaultLevel()
{
switch (ItemType)
@ -68,10 +72,15 @@ namespace Campofinale.Game.Inventory
}
public ItemValuableDepotType ItemType
{
get{
get
{
return ResourceManager.GetItemTable(id).valuableTabType;
}
}
public ItemTable GetItemTable()
{
return ResourceManager.GetItemTable(id);
}
public virtual ScdItemGrid ToProto()
{
try

View File

@ -379,7 +379,15 @@ namespace Campofinale.Game
if (!en.spawned)
{
en.spawned = true;
GetOwner().Send(new PacketScObjectEnterView(GetOwner(), new List<Entity>() { en }));
try
{
GetOwner().Send(new PacketScObjectEnterView(GetOwner(), new List<Entity>() { en }));
}
catch(Exception e)
{
}
}
}
else

View File

@ -46,6 +46,7 @@ namespace Campofinale.Game.Spaceship
IsWorking = isWorking,
PhysicalStrength = physicalStrength,
StationedRoomId = stationedRoomId,
Skills =
{
new ScdSpaceshipCharSkill()

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using MongoDB.Bson.Serialization.IdGenerators;
using static Campofinale.Resource.ResourceManager;
using Campofinale.Resource;
using Campofinale.Resource.Table;
namespace Campofinale.Game.Spaceship
{
@ -84,6 +85,39 @@ namespace Campofinale.Game.Spaceship
}
}
}
public void GiftToChar(CsSpaceshipPresentGiftToChar req)
{
SpaceshipChar chara = GetChar(req.CharId);
if (chara != null)
{
foreach (var item in req.Gifts)
{
GiftItemTable giftItem = ResourceManager.giftItemTable[item.Id];
chara.favorability += giftItem.favorablePoint * item.Count;
//TODO item consume
}
ScSpaceshipPresentGiftToChar confirm = new()
{
CurFav = chara.favorability,
CharId = chara.id,
RecvGiftCnt = req.Gifts.Count,
};
//TODO packet class
/*ScSpaceshipCharFavorabilityChange change = new()
{
ChangeInfos =
{
new SpaceshipCharFavorabilityChangeInfo()
{
CharId = chara.id,
CurFav=chara.favorability,
}
}
};*/
owner.Send(Protocol.ScMsgId.ScSpaceshipPresentGiftToChar, confirm);
}
}
}

View File

@ -65,7 +65,17 @@ namespace Campofinale.Http
await data(ctx);
}
[StaticRoute(HttpServerLite.HttpMethod.GET, "/serverStatus")]
public static async Task serverStatus(HttpContext ctx)
{
string resp = "{\"maxPlayers\":" + Server.config.serverOptions.maxPlayers + ", \"players\":" + Server.clients.Count + ", \"status\":\"Online\", \"gameVersion\": \"" + GameConstants.GAME_VERSION + "\", \"serverVersion\": \"" + Server.ServerVersion + "\"}";
ctx.Response.StatusCode = 200;
ctx.Response.ContentLength = resp.Length;
ctx.Response.ContentType = "application/json";
await ctx.Response.SendAsync(resp);
}
[StaticRoute(HttpServerLite.HttpMethod.POST, "/u8/pay/getAllProductList")]
public static async Task getAllProductList(HttpContext ctx)
{

View File

@ -30,7 +30,8 @@ namespace Campofinale.Http
[StaticRoute(HttpServerLite.HttpMethod.GET, "/pcSdk/console")]
public static async Task ConsoleResponce(HttpContext ctx)
{
string cmd = ctx.Request.Query.Elements["command"].Replace("+"," ");
string encodedCmd = Uri.UnescapeDataString(ctx.Request.Query.Elements["command"]);
string cmd = Encoding.UTF8.GetString(Convert.FromBase64String(encodedCmd));
string token = ctx.Request.Query.Elements["token"];
string message = "";
string[] split = cmd.Split(" ");

View File

@ -20,6 +20,10 @@ public static class Logger
var method = frame?.GetMethod();
return method?.DeclaringType?.Name ?? "Server";
}
/// <summary>
/// Print a text in the console
/// </summary>
/// <param name="text"></param>
public static void Print(string text)
{
string className = GetCallingClassName();
@ -27,6 +31,10 @@ public static class Logger
string prefix = "<" + "INFO".Pastel("03fcce") + $":{className.Pastel("999")}>";
Console.WriteLine($"{prefix} " + text);
}
/// <summary>
/// Print a text in the console as Error
/// </summary>
/// <param name="text"></param>
public static void PrintError(string text)
{
string className = GetCallingClassName();
@ -34,6 +42,10 @@ public static class Logger
string prefix = "<" + "ERROR".Pastel("eb4034") + $":{className.Pastel("999")}>";
Console.WriteLine($"{prefix} " + text.Pastel("917e7e"));
}
/// <summary>
/// Print a text in the console as a Warn
/// </summary>
/// <param name="text"></param>
public static void PrintWarn(string text)
{
string className = GetCallingClassName();
@ -54,7 +66,10 @@ public static class Logger
Logger.hideLogs = hideLogs;
logWriter = new StreamWriter("latest.log", false);
}
/// <summary>
/// Log a message
/// </summary>
/// <param name="message"></param>
public static void Log(string message)
{
if (!hideLogs)

View File

@ -61,6 +61,11 @@ namespace Campofinale.Network
byte networkValue = buf[index];
return networkValue;
}
/// <summary>
/// Parse the body using a specific IMessage proto class
/// </summary>
/// <typeparam name="TBody"></typeparam>
/// <returns></returns>
public TBody DecodeBody<TBody>() where TBody : IMessage<TBody>, new()
{
return new MessageParser<TBody>(() => new()).ParseFrom(finishedBody);
@ -76,35 +81,10 @@ namespace Campofinale.Network
Buffer.BlockCopy(source, 0, destination, offset, source.Length);
}
public static byte[] ToByteArray(IntPtr ptr, int length)
{
if (ptr == IntPtr.Zero)
{
throw new ArgumentException("Pointer cannot be null", nameof(ptr));
}
byte[] byteArray = new byte[length];
Marshal.Copy(ptr, byteArray, 0, length);
return byteArray;
}
public static IntPtr ByteArrayToIntPtr(byte[] data)
{
if (data == null) throw new ArgumentNullException(nameof(data));
// Allocate unmanaged memory
IntPtr ptr = Marshal.AllocHGlobal(data.Length);
// Copy the byte array to the unmanaged memory
Marshal.Copy(data, 0, ptr, data.Length);
return ptr;
}
public static byte[] EncryptWithPublicKey(byte[] data, string publicKey)
{
// Crea un oggetto RSA
using (RSA rsa = RSA.Create())
{
publicKey = publicKey.Replace("-----BEGIN PUBLIC KEY-----", "");
publicKey = publicKey.Replace("\r", "");
publicKey = publicKey.Replace("\n", "");
@ -112,24 +92,44 @@ namespace Campofinale.Network
publicKey = publicKey.Trim();
Logger.Print(publicKey);
byte[] publicKey_ = Convert.FromBase64String(publicKey);
// Importa la chiave pubblica
rsa.ImportSubjectPublicKeyInfo(publicKey_, out _);
// Crittografa i dati
return rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
}
}
/// <summary>
/// Set the data of the packet with the Message Id and the body
/// </summary>
/// <param name="msgId"></param>
/// <param name="body">The proto message</param>
/// <returns>The current Packet</returns>
public Packet SetData(ScMsgId msgId, IMessage body)
{
set_body = body;
cmdId = (int)msgId;
return this;
}
/// <summary>
/// Encode the packet using the Packet class
/// </summary>
/// <param name="packet">The packet</param>
/// <param name="seq">the sequence id</param>
/// <param name="totalPackCount">the pack count</param>
/// <param name="currentPackIndex"></param>
/// <returns></returns>
public static byte[] EncodePacket(Packet packet,ulong seq = 0, uint totalPackCount = 1, uint currentPackIndex = 0)
{
return EncodePacket(packet.cmdId,packet.set_body,seq, totalPackCount, currentPackIndex);
}
public static ulong seqNext = 1;
/// <summary>
/// Encode the packet using the msgId and the body
/// </summary>
/// <param name="msgId"></param>
/// <param name="body"></param>
/// <param name="seqNext_"></param>
/// <param name="totalPackCount"></param>
/// <param name="currentPackIndex"></param>
/// <returns></returns>
public static byte[] EncodePacket(int msgId, IMessage body, ulong seqNext_ = 0, uint totalPackCount=1,uint currentPackIndex=0)
{
if (seqNext_ == 0)
@ -149,6 +149,15 @@ namespace Campofinale.Network
return data;
}
/// <summary>
/// Encode the packet with msgId and body as byte array
/// </summary>
/// <param name="msgId"></param>
/// <param name="body"></param>
/// <param name="seqNext_"></param>
/// <param name="totalPackCount"></param>
/// <param name="currentPackIndex"></param>
/// <returns></returns>
public static byte[] EncodePacket(int msgId, byte[] body, ulong seqNext_ = 0, uint totalPackCount = 1, uint currentPackIndex = 0)
{
if (seqNext_ == 0)
@ -172,6 +181,12 @@ namespace Campofinale.Network
return data;
}
/// <summary>
/// Read the byteArray as a valid packet
/// </summary>
/// <param name="client"></param>
/// <param name="byteArray"></param>
/// <returns>The decoded packet</returns>
public static Packet Read(Player client,byte[] byteArray)
{
byte headLength = GetByte(byteArray, 0);

View File

@ -26,6 +26,7 @@ namespace Campofinale.Packets.Cs
if (character != null)
{
character.potential=req.Level;
//TODO consume Item ID
ScCharPotentialUnlock unlock = new()

View File

@ -0,0 +1,27 @@
using Campofinale.Network;
using Campofinale.Packets.Sc;
using Campofinale.Protocol;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Campofinale.Packets.Cs
{
public class HandleCsItemBagAbandonInBag
{
[Server.Handler(CsMsgId.CsItemBagAbandonInBag)]
public static void Handle(Player session, CsMsgId cmdId, Packet packet)
{
CsItemBagAbandonInBag req = packet.DecodeBody<CsItemBagAbandonInBag>();
session.inventoryManager.DropItemsBag(req);
}
}
}

View File

@ -0,0 +1,27 @@
using Campofinale.Network;
using Campofinale.Packets.Sc;
using Campofinale.Protocol;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Campofinale.Packets.Cs
{
public class HandleCsItemBagMoveInBag
{
[Server.Handler(CsMsgId.CsItemBagMoveInBag)]
public static void Handle(Player session, CsMsgId cmdId, Packet packet)
{
CsItemBagMoveInBag req = packet.DecodeBody<CsItemBagMoveInBag>();
session.inventoryManager.items.MoveBagItem(req.FromGrid, req.ToGrid);
}
}
}

View File

@ -65,9 +65,18 @@ namespace Campofinale.Packets.Cs
session.Disconnect();
return;
}
session.Load(account.id);
bool exist=session.Load(account.id);
rsp.Uid = ""+session.accountId;
if (!exist)
{
rsp.IsFirstLogin = true;
//session.gender = Gender.GenInvalid;
//session.Send(ScMsgId.ScLogin, rsp);
//session.Send(new PacketScSyncBaseData(session));
//return;
}
session.Send(ScMsgId.ScLogin, rsp);
}
@ -232,7 +241,7 @@ namespace Campofinale.Packets.Cs
session.Send(new PacketScSpaceshipSync(session));
session.Send(new PacketScSyncFullDungeonStatus(session));
session.Send(new PacketScActivitySync(session));
session.Send(new PacketScSnsGetChatList(session));
session.Send(ScMsgId.ScSyncFullDataEnd, new ScSyncFullDataEnd());
session.EnterScene();
session.Initialized = true;

View File

@ -0,0 +1,28 @@
using Campofinale.Network;
using Campofinale.Packets.Sc;
using Campofinale.Protocol;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Campofinale.Packets.Cs
{
public class HandleCsSpaceshipPresentGiftToChar
{
[Server.Handler(CsMsgId.CsSpaceshipPresentGiftToChar)]
public static void Handle(Player session, CsMsgId cmdId, Packet packet)
{
CsSpaceshipPresentGiftToChar req = packet.DecodeBody<CsSpaceshipPresentGiftToChar>();
session.spaceshipManager.GiftToChar(req);
}
}
}

View File

@ -15,6 +15,11 @@ namespace Campofinale.Packets.Sc
public PacketScItemBagScopeModify(Player client, Item item) {
if (item == null)
{
SetData(ScMsgId.ScItemBagScopeModify, new ScItemBagScopeModify());
return;
}
ScItemBagScopeModify proto = new ScItemBagScopeModify()
{
Depot =

View File

@ -19,10 +19,10 @@ namespace Campofinale.Packets.Sc
{
Bag = new()
{
GridLimit = 30,
GridLimit = client.inventoryManager.items.maxBagSize,
Grids =
{
new ScdItemGrid()
/*new ScdItemGrid()
{
GridIndex=0,
Count=1,
@ -79,7 +79,7 @@ namespace Campofinale.Packets.Sc
InstId=300000000004,
},
}
}*/
}
},
FactoryDepot =
@ -114,7 +114,18 @@ namespace Campofinale.Packets.Sc
proto.Bag = null;
}
proto.Depot.Add(i, new ScdItemDepot());
List<Item> items = client.inventoryManager.items.FindAll(item => item.ItemType == (ItemValuableDepotType)i);
if(proto.Bag!=null)
foreach (var item in client.inventoryManager.items.bag)
{
proto.Bag.Grids.Add(new ScdItemGrid()
{
Count=item.Value.amount,
GridIndex=item.Key,
Id=item.Value.id,
Inst=item.Value.ToProto().Inst,
});
}
List<Item> items = client.inventoryManager.items.items.FindAll(item => item.ItemType == (ItemValuableDepotType)i);
items.ForEach(item =>
{
if (item.InstanceType())

View File

@ -74,7 +74,7 @@ namespace Campofinale.Packets.Sc
ScriptId = l.scriptId,
IsDone = false,
State = 1,
};
int i = 0;
foreach (var item in l.properties)

View File

@ -0,0 +1,33 @@
using Campofinale.Network;
using Campofinale.Protocol;
using Campofinale.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Packets.Sc
{
public class PacketScSnsGetChatList : Packet
{
public PacketScSnsGetChatList(Player player) {
ScSnsGetChatList proto = new ScSnsGetChatList() {
};
foreach (var chat in ResourceManager.snsChatTable)
{
var chatInfo = new SnsChatInfo()
{
ChatId = chat.Value.chatId,
ChatType = chat.Value.chatType,
Timestamp = DateTime.UtcNow.ToUnixTimestampMilliseconds(),
};
proto.ChatList.Add(chatInfo);
}
SetData(ScMsgId.ScSnsGetChatList, proto);
}
}
}

View File

@ -20,7 +20,7 @@ namespace Campofinale.Packets.Sc
Level = client.level,
Exp=client.xp,
RoleName = client.nickname,
Gender = Gender.GenFemale,
Gender = client.gender,
ShortId="1",
};

View File

@ -89,6 +89,7 @@ namespace Campofinale
public string accountId = "";
public string nickname = "Endministrator";
public ulong roleId= 1;
public Gender gender=Gender.GenFemale;
public uint level = 20;
public uint xp = 0;
//
@ -139,7 +140,7 @@ namespace Campofinale
{
return chars.FindAll(c=> teams[teamIndex].members.Contains(c.guid));
}
public void Load(string accountId)
public bool Load(string accountId)
{
this.accountId = accountId;
PlayerData data = DatabaseManager.db.GetPlayerById(this.accountId);
@ -161,9 +162,12 @@ namespace Campofinale
maxDashEnergy = data.maxDashEnergy;
curStamina = data.curStamina;
nextRecoverTime=data.nextRecoverTime;
if (data.gender > 0) gender = data.gender;
LoadCharacters();
mails = DatabaseManager.db.LoadMails(roleId);
inventoryManager.Load();
if (data.bag != null) inventoryManager.items.bag = data.bag;
spaceshipManager.Load();
if (data.scenes != null)
{
@ -178,38 +182,62 @@ namespace Campofinale
}
sceneManager.Load();
factoryManager.Load();
return (data != null);
}
public void LoadCharacters()
{
chars = DatabaseManager.db.LoadCharacters(roleId);
}
//Added in 1.0.7
/// <summary>
/// Get the character using the guid *Added in 1.0.7*
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
public Character GetCharacter(ulong guid)
{
return chars.Find(c => c.guid == guid);
}
/// <summary>
/// Get the character using the template id
/// </summary>
/// <param name="templateId"></param>
/// <returns>Character</returns>
public Character GetCharacter(string templateId)
{
return chars.Find(c => c.id==templateId);
}
public void Initialize()
{
foreach (var item in ResourceManager.characterTable)
if (Server.config.serverOptions.defaultCharacters.giveAllCharacters)
{
chars.Add(new Character(roleId,item.Key,20));
foreach (var item in ResourceManager.characterTable)
{
chars.Add(new Character(roleId, item.Key, Server.config.serverOptions.defaultCharacters.defaultLevel));
}
}
else
{
foreach (var item in Server.config.serverOptions.defaultCharacters.characters)
{
chars.Add(new Character(roleId, item, Server.config.serverOptions.defaultCharacters.defaultLevel));
}
}
foreach(var item in itemTable)
{
if(item.Value.maxStackCount == -1)
if(item.Value.GetStorage()!= ItemStorageSpace.BagAndFactoryDepot)
{
inventoryManager.items.Add(new Item(roleId, item.Value.id, 10000000));
}
else
{
inventoryManager.items.Add(new Item(roleId, item.Value.id, item.Value.maxStackCount));
if (item.Value.maxStackCount == -1)
{
inventoryManager.items.Add(new Item(roleId, item.Value.id, 10000000));
}
else
{
inventoryManager.items.Add(new Item(roleId, item.Value.id, item.Value.maxStackCount));
}
}
}
teams.Add(new Team()
{

View File

@ -0,0 +1,100 @@
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Resource
{
public class ResourceLoader
{
/// <summary>
/// Load table cfg automatically inside ResourceManager fields
/// </summary>
public static void LoadTableCfg()
{
var tableCfgTypes = GetAllTableCfgTypes();
foreach (var type in tableCfgTypes)
{
var attr = type.GetCustomAttribute<TableCfgTypeAttribute>();
string json = ResourceManager.ReadJsonFile(attr.Name);
FieldInfo field = GetResourceField(type);
if (field != null && json.Length > 0)
{
object deserializedData = DeserializeJson(json, field.FieldType, type);
field.SetValue(null, deserializedData);
//Logger.Print($"Loaded {attr.Name} into {field.Name}");
}
}
}
private static object DeserializeJson(string json, Type fieldType, Type valueType)
{
if (fieldType.IsGenericType)
{
var genericTypeDef = fieldType.GetGenericTypeDefinition();
if (genericTypeDef == typeof(Dictionary<,>))
{
var keyType = fieldType.GetGenericArguments()[0];
var valType = fieldType.GetGenericArguments()[1];
return JsonConvert.DeserializeObject(json, typeof(Dictionary<,>).MakeGenericType(keyType, valType));
}
else if (genericTypeDef == typeof(List<>))
{
return JsonConvert.DeserializeObject(json, typeof(List<>).MakeGenericType(valueType));
}
}
return JsonConvert.DeserializeObject(json, valueType);
}
public static List<Type> GetAllTableCfgTypes()
{
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.GetCustomAttribute<TableCfgTypeAttribute>() != null)
.ToList();
}
public static FieldInfo GetResourceField(Type type)
{
var resourceManagerType = typeof(ResourceManager);
var fields = resourceManagerType.GetFields(BindingFlags.Public | BindingFlags.Static);
foreach (var field in fields)
{
var fieldType = field.FieldType;
if (fieldType.IsGenericType)
{
var genericTypeDef = fieldType.GetGenericTypeDefinition();
if (genericTypeDef == typeof(Dictionary<,>))
{
var valueType = fieldType.GetGenericArguments()[1];
if (valueType == type)
{
return field;
}
}
else if (genericTypeDef == typeof(List<>) && fieldType.GetGenericArguments()[0] == type)
{
return field;
}
}
else
{
if (fieldType == type)
{
return field;
}
}
}
return null;
}
}
}

View File

@ -28,19 +28,19 @@ namespace Campofinale.Resource
public class ResourceManager
{
public static Dictionary<string, SceneAreaTable> sceneAreaTable = new();
public static StrIdNumTable strIdNumTable = new StrIdNumTable();
public static Dictionary<string, CharacterTable> characterTable = new();
public static Dictionary<string, SystemJumpTable> systemJumpTable = new();
public static StrIdNumTable strIdNumTable = new StrIdNumTable();//
public static Dictionary<string, CharacterTable> characterTable = new(); //
public static Dictionary<string, SystemJumpTable> systemJumpTable = new(); //
public static Dictionary<string, SettlementBasicDataTable> settlementBasicDataTable = new();
public static Dictionary<string, BlocMissionTable> blocMissionTable = new();
public static MissionAreaTable missionAreaTable = new();
public static MissionAreaTable missionAreaTable = new(); //
public static Dictionary<string, DialogTextTable> dialogTextTable = new();
public static Dictionary<string, GameSystemConfigTable> gameSystemConfigTable = new();
public static Dictionary<string, WikiGroupTable> wikiGroupTable = new();
public static Dictionary<string, object> blocUnlockTable = new();
public static Dictionary<string, GameMechanicTable> gameMechanicTable = new();
public static Dictionary<string, WeaponBasicTable> weaponBasicTable= new();
public static Dictionary<string, BlocDataTable> blocDataTable = new();
public static Dictionary<string, BlocDataTable> blocDataTable = new(); //
public static Dictionary<string, ItemTable> itemTable = new();
public static Dictionary<string, DomainDataTable> domainDataTable = new();
public static Dictionary<string, CollectionTable> collectionTable = new();
@ -59,15 +59,18 @@ namespace Campofinale.Resource
public static Dictionary<string, SpaceShipCharBehaviourTable> spaceShipCharBehaviourTable = new();
public static Dictionary<string, SpaceshipRoomInsTable> spaceshipRoomInsTable = new();
public static Dictionary<string, DungeonTable> dungeonTable = new();
public static Dictionary<string, LevelGradeTable> levelGradeTable = new();
public static Dictionary<string, LevelGradeTable> levelGradeTable = new(); //
public static Dictionary<string, RewardTable> rewardTable = new();
public static Dictionary<string, AdventureTaskTable> adventureTaskTable = new();
public static StrIdNumTable dialogIdTable = new();
public static DialogIdTable dialogIdTable = new();//
public static Dictionary<string, LevelShortIdTable> levelShortIdTable = new();
public static Dictionary<string, FactoryBuildingTable> factoryBuildingTable = new();
public static Dictionary<string, FacSTTNodeTable> facSTTNodeTable = new();
public static Dictionary<string, FacSTTLayerTable> facSTTLayerTable = new();
public static InteractiveTable interactiveTable = new();
public static Dictionary<int, ItemTypeTable> itemTypeTable = new(); //
public static Dictionary<string, SNSChatTable> snsChatTable = new();//
public static Dictionary<string, GiftItemTable> giftItemTable = new();
public static InteractiveTable interactiveTable = new(); //
public static List<LevelScene> levelDatas = new();
public static List<InteractiveData> interactiveData = new();
@ -77,6 +80,11 @@ namespace Campofinale.Resource
return levelDatas.Find(a => a.id == name).idNum;
}
public static bool missingResources = false;
/// <summary>
/// Utility method for read a json file
/// </summary>
/// <param name="path">The file path</param>
/// <returns>Return the file content if the file exist, else it return an empty string</returns>
public static string ReadJsonFile(string path)
{
try
@ -103,7 +111,7 @@ namespace Campofinale.Resource
dialogTextTable = JsonConvert.DeserializeObject<Dictionary<string, DialogTextTable>>(ReadJsonFile("TableCfg/DialogTextTable.json"));
gameSystemConfigTable = JsonConvert.DeserializeObject<Dictionary<string, GameSystemConfigTable>>(ReadJsonFile("TableCfg/GameSystemConfigTable.json"));
wikiGroupTable = JsonConvert.DeserializeObject<Dictionary<string, WikiGroupTable>>(ReadJsonFile("TableCfg/WikiGroupTable.json"));
dialogIdTable = JsonConvert.DeserializeObject<StrIdNumTable>(ReadJsonFile("Json/GameplayConfig/DialogIdTable.json"));
dialogIdTable = JsonConvert.DeserializeObject<DialogIdTable>(ReadJsonFile("Json/GameplayConfig/DialogIdTable.json"));
blocUnlockTable = JsonConvert.DeserializeObject<Dictionary<string, object>>(ReadJsonFile("TableCfg/BlocUnlockTable.json"));
gameMechanicTable= JsonConvert.DeserializeObject<Dictionary<string, GameMechanicTable>>(ReadJsonFile("TableCfg/GameMechanicTable.json"));
weaponBasicTable = JsonConvert.DeserializeObject<Dictionary<string, WeaponBasicTable>>(ReadJsonFile("TableCfg/WeaponBasicTable.json"));
@ -134,10 +142,12 @@ namespace Campofinale.Resource
factoryBuildingTable = JsonConvert.DeserializeObject<Dictionary<string, FactoryBuildingTable>>(ReadJsonFile("TableCfg/FactoryBuildingTable.json"));
facSTTNodeTable = JsonConvert.DeserializeObject<Dictionary<string, FacSTTNodeTable>>(ReadJsonFile("TableCfg/FacSTTNodeTable.json"));
facSTTLayerTable = JsonConvert.DeserializeObject<Dictionary<string, FacSTTLayerTable>>(ReadJsonFile("TableCfg/FacSTTLayerTable.json"));
itemTypeTable = JsonConvert.DeserializeObject<Dictionary<int, ItemTypeTable>>(ReadJsonFile("TableCfg/ItemTypeTable.json"));
interactiveTable = JsonConvert.DeserializeObject<InteractiveTable>(ReadJsonFile("Json/Interactive/InteractiveTable.json"));
LoadInteractiveData();
LoadLevelDatas();
LoadLevelDatas();
ResourceLoader.LoadTableCfg();
if (missingResources)
{
Logger.PrintWarn("Missing some resources. The gameserver will probably crash.");
@ -406,15 +416,7 @@ namespace Campofinale.Resource
{
public List<WikiGroup> list;
}
public class InteractiveTable
{
public Dictionary<string, InteractiveTemplate> interactiveDataDict = new();
public class InteractiveTemplate
{
public string templateId;
}
}
public class WikiGroup
{
public string groupId;
@ -783,19 +785,6 @@ namespace Campofinale.Resource
public string settlementId;
public string domainId;
}
public class StrIdNumTable
{
public StrIdDic skill_group_id;
public StrIdDic item_id;
public Dictionary<string, int> dialogStrToNum;
public StrIdDic chapter_map_id;
public StrIdDic char_voice_id;
public StrIdDic char_doc_id;
public StrIdDic area_id;
public StrIdDic map_mark_temp_id;
public StrIdDic wiki_id;
public StrIdDic client_game_var_string_id;
}
public class GachaCharPoolTable
{
public string id;
@ -823,10 +812,7 @@ namespace Campofinale.Resource
{
}
public class SystemJumpTable
{
public int bindSystem;
}
public class StrIdDic
{
public Dictionary<string, int> dic;
@ -843,6 +829,7 @@ namespace Campofinale.Resource
public string enemyId;
public string templateId;
}
public class ItemTable
{
public ItemValuableDepotType valuableTabType;
@ -850,6 +837,12 @@ namespace Campofinale.Resource
public int maxStackCount;
public bool backpackCanDiscard;
public string modelKey;
public int type;
public ItemStorageSpace GetStorage()
{
return ResourceManager.itemTypeTable[type].storageSpace;
}
}
public class WeaponBasicTable
{
@ -908,15 +901,7 @@ namespace Campofinale.Resource
public string id;
public int count;
}
public class CharacterTable
{
public List<Attributes> attributes;
public string charId;
public int weaponType;
public string engName;
public int rarity;
}
public class Attributes
{
public int breakStage;

View File

@ -6,7 +6,8 @@ using System.Threading.Tasks;
namespace Campofinale.Resource.Table
{
public class BlocDataTable
[TableCfgType("TableCfg/BlocDataTable.json", LoadPriority.LOW)]
public class BlocDataTable : TableCfgResource
{
public string blocId;
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Campofinale.Resource.ResourceManager;
namespace Campofinale.Resource.Table
{
[TableCfgType("TableCfg/CharacterTable.json", LoadPriority.LOW)]
public class CharacterTable : TableCfgResource
{
public List<Attributes> attributes;
public string charId;
public int weaponType;
public string engName;
public int rarity;
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Campofinale.Resource.ResourceManager;
namespace Campofinale.Resource.Table
{
[TableCfgType("Json/GameplayConfig/DialogIdTable.json", LoadPriority.LOW)]
public class DialogIdTable : StrIdNumTable
{
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Resource.Table
{
[TableCfgType("TableCfg/GiftItemTable.json", LoadPriority.LOW)]
public class GiftItemTable
{
public int favorablePoint;
public string id;
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Resource.Table
{
[TableCfgType("Json/Interactive/InteractiveTable.json", LoadPriority.LOW)]
public class InteractiveTable : TableCfgResource
{
public Dictionary<string, InteractiveTemplate> interactiveDataDict = new();
public class InteractiveTemplate
{
public string templateId;
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Resource.Table
{
[TableCfgType("TableCfg/ItemTypeTable.json", LoadPriority.LOW)]
public class ItemTypeTable
{
public int itemType;
public ItemStorageSpace storageSpace;
}
}

View File

@ -6,7 +6,8 @@ using System.Threading.Tasks;
namespace Campofinale.Resource.Table
{
public class LevelGradeTable
[TableCfgType("TableCfg/LevelGradeTable.json", LoadPriority.LOW)]
public class LevelGradeTable : TableCfgResource
{
public string name;
public List<LevelGradeInfo> grades;

View File

@ -6,7 +6,8 @@ using System.Threading.Tasks;
namespace Campofinale.Resource.Table
{
public class MissionAreaTable
[TableCfgType("Json/GameplayConfig/MissionAreaTable.json", LoadPriority.LOW)]
public class MissionAreaTable : TableCfgResource
{
public Dictionary<string, Dictionary<string, object>> m_areas;
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Resource.Table
{
[TableCfgType("TableCfg/SNSChatTable.json", LoadPriority.LOW)]
public class SNSChatTable
{
public string chatId;
public int chatType;
public int tagType;
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Campofinale.Resource.ResourceManager;
namespace Campofinale.Resource.Table
{
[TableCfgType("TableCfg/StrIdNumTable.json", LoadPriority.LOW)]
public class StrIdNumTable : TableCfgResource
{
public StrIdDic skill_group_id;
public StrIdDic item_id;
public Dictionary<string, int> dialogStrToNum;
public StrIdDic chapter_map_id;
public StrIdDic char_voice_id;
public StrIdDic char_doc_id;
public StrIdDic area_id;
public StrIdDic map_mark_temp_id;
public StrIdDic wiki_id;
public StrIdDic client_game_var_string_id;
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Resource.Table
{
[TableCfgType("TableCfg/SystemJumpTable.json", LoadPriority.LOW)]
public class SystemJumpTable
{
public int bindSystem;
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Campofinale.Resource
{
public abstract class TableCfgResource
{
/// <summary>
/// Not implemented yet
/// </summary>
public void OnLoad()
{
}
}
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class TableCfgTypeAttribute : Attribute
{
/// <summary>
/// Path of the Resource
/// </summary>
public string Name { get; }
/// <summary>
/// Priority of load (still not implemented)
/// </summary>
public LoadPriority Priority { get; }
public TableCfgTypeAttribute(string name, LoadPriority priority)
{
Name = name;
Priority = priority;
}
}
public enum LoadPriority
{
HIGH,
MEDIUM,
LOW
}
}

View File

@ -1,10 +1,10 @@
# Campofinale
[EN](README.md) | [IT](docs/README_it-IT.md) | [RU](docs/README_ru-RU.md) | [CN](docs/README_zh-CN.md) | [NL](docs/README_nl-NL.md)
![Logo]()
Campofinale is a experimental server implementation for a certain factory building game.
> **NOTICE: Our old Discord server was raided (all members were removed), please rejoin our community using this new invite link: https://discord.gg/YZGYtAxeZk**
## Current Features
* Login
@ -29,8 +29,8 @@ New tutorial will be added in the next days on the wiki, in the meanwhile you ca
## Discord for support
If you want to discuss, ask for support or help with this project, join our [Discord Server](https://discord.gg/wbqqFYaNzD)!
If you want to discuss, ask for support or help with this project, join our [Discord Server](https://discord.gg/YZGYtAxeZk)!
## Note
This project is developed independently, and all rights to the original game assets and intellectual property belong to their respective owners.
This project is developed independently, and all rights to the original game assets and intellectual property belong to their respective owners.

View File

@ -24,5 +24,5 @@
| idlist | 显示所有角色(chars)、敌人(enemies)和场景(scenes)的id | `<chars\|enemies\|scenes>` | 否 | idlist `<chars\|enemies\|scenes>` | idlist chars |
---
> [!WARNING]警告
> [!WARNING]
> `level` 指令: 如果你没有指定具体的`id`字段, 那么等级变化将会被应用在**所有**武器和干员身上

View File

@ -25,7 +25,7 @@
| eny_0059_erhound | Blighted Tuskbeast| 侵蚀牙兽 |
| eny_0060_lbmad | Blighted Klaw| 侵蚀爪牙 |
| eny_0061_palecore | Marble Agellomoirai| 白垩界卫一阶段 |
| eny_0062_paletent | Marble Appendage| 白垩界卫一阶段触手 |
| eny_0062_paletent | Marble Appendage| 白垩附肢 |
| eny_0063_agmelee2 | Ram α | 大角天使α |
| eny_0064_agrange2 | Sting α | 针刺天使α |
| eny_0065_lbmob2 | Elite Raider | 精锐劫掠者 |

View File

@ -1,10 +1,10 @@
# Campofinale
[EN](/README.md) | [IT](./README_it-IT.md) | [RU](./README_ru-RU.md) | [CN](./README_zh-CN.md) | [NL](./README_nl-NL.md)
![Logo]()
Campofinale - экспериментальная реализация сервера для кое-какой игры по постройке фабрик.
> **Внимание: наш старый Discord сервер был подвергнут крашу (все участники были выгнаны), поэтому настоятельно просим вас присоединиться заново по этой ссылке: https://discord.gg/eGGXymVd4K**
## Текущие возможности
* Переключение персонажей;
@ -25,7 +25,7 @@ Campofinale - экспериментальная реализация серве
## Discord поддержка
Если вы хотите обсудить проект или помочь в его разработке, присоединяйтесь к нашему [Discord серверу](https://discord.gg/gPvqhfdMU6)!
Если вы хотите обсудить проект или помочь в его разработке, присоединяйтесь к нашему [Discord серверу](https://discord.gg/eGGXymVd4K)!
## Примечание

43
docs/README_zh-CN.md Normal file
View File

@ -0,0 +1,43 @@
# Campofinale
[EN](../README.md) | [IT](./README_it-IT.md) | [RU](./README_ru-RU.md) | [CN](./README_zh-CN.md) | [NL](./README_nl-NL.md)
![Logo]()
Campofinale 是为某个工厂建造游戏提供的实验性本地服务器实现
> **我们的旧 Discord 服务器遭到了袭击(攻击者踢走了所有成员),请使用以下新邀请链接重新加入我们的社区:[https://discord.gg/eGGXymVd4K](https://discord.gg/eGGXymVd4K)**
## 当前功能
* 登录
* 切换角色
* 切换配队
* 场景切换
* 通过MongoDB保存存档
* 战斗系统
## 补充信息
您可以在[这里](./CommandList/commands_zh-CN.md)找到所有服务端指令的详细说明。
所有场景的列表[在此](./LevelsTable.md)。
所有敌人的列表[在此](./EnemiesTable.md)。
所有干员的列表[在此](./CharactersTable.md)。
所有物品的列表[在此](./ItemsTable_zh-CN.md)。
如果你想使用游戏内控制台,请前往 `设置 → 平台与账号 → 账号设置("点击前往"`。要查看可用命令,请输入 `help`
## 教程
新的教程将会在几天后添加到Wiki上目前您可以在Discord服务器上寻求帮助
## 在Discord上寻求帮助
如果你想讨论、寻求帮助或者协助我们完善和改进此项目,请加入我们的[Discord服务器](https://discord.gg/eGGXymVd4K)!
## 附录
本项目为独立开发,所有原始游戏资产和知识产权均归其各自所有者所有