initial farm implementation

This commit is contained in:
Spirtix 2023-07-20 22:11:42 +02:00
parent 1c01eb25f5
commit f71fbe3559
16 changed files with 1513 additions and 2316 deletions

View File

@ -60,7 +60,11 @@ methods = [
'GetAnnouncementsByUser',
'GetUserRoomItemPositions',
'SetUserRoomItemPositions',
'GetAverageRatingForRoom'
'GetAverageRatingForRoom',
'GetUserRoomList',
'GetUserActivityByUserID',
'SetNextItemState',
'SetUserRoom'
]
def routable(path):

View File

@ -579,7 +579,7 @@ public class ContentController : Controller {
[Route("ContentWebService.asmx/GetBuddyList")]
public IActionResult GetBuddyList() {
// TODO: this is a placeholder
return Ok(new BuddyList[0]);
return Ok(new BuddyList { Buddy = new Buddy[0] });
}
[HttpPost]
@ -662,9 +662,11 @@ public class ContentController : Controller {
[Produces("application/xml")]
[Route("ContentWebService.asmx/GetUserRoomItemPositions")]
public IActionResult GetUserRoomItemPositions([FromForm] string apiToken, [FromForm] string roomID) {
if (roomID is null)
roomID = "";
Room? room = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking?.Rooms.FirstOrDefault(x => x.RoomId == roomID);
if (room is null)
return Ok();
return Ok(new UserItemPositionList { UserItemPosition = new UserItemPosition[0] });
return Ok(roomService.GetUserItemPositionList(room));
}
@ -672,21 +674,30 @@ public class ContentController : Controller {
[Produces("application/xml")]
[Route("ContentWebService.asmx/SetUserRoomItemPositions")]
public IActionResult SetUserRoomItemPositions([FromForm] string apiToken, [FromForm] string createXml, [FromForm] string updateXml, [FromForm] string removeXml, [FromForm] string roomID) {
if (roomID is null)
roomID = "";
Room? room = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking?.Rooms.FirstOrDefault(x => x.RoomId == roomID);
if (room is null)
return Ok();
if (room is null) {
room = new Room {
RoomId = roomID,
Items = new List<RoomItem>()
};
ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking?.Rooms.Add(room);
ctx.SaveChanges();
}
UserItemPositionSetRequest[] createItems = XmlUtil.DeserializeXml<UserItemPositionSetRequest[]>(createXml);
UserItemPositionSetRequest[] updateItems = XmlUtil.DeserializeXml<UserItemPositionSetRequest[]>(updateXml);
int[] deleteItems = XmlUtil.DeserializeXml<int[]>(removeXml);
int[] ids = roomService.CreateItems(createItems, room);
Tuple<int[], UserItemState[]> createData = roomService.CreateItems(createItems, room);
UserItemState[] state = roomService.UpdateItems(updateItems, room);
roomService.DeleteItems(deleteItems, room);
UserItemPositionSetResponse response = new UserItemPositionSetResponse {
Success = true,
CreatedUserItemPositionIDs = ids,
CreatedUserItemPositionIDs = createData.Item1,
UserItemStates = createData.Item2,
Result = ItemPositionValidationResult.Valid
};
@ -696,6 +707,66 @@ public class ContentController : Controller {
return Ok(response);
}
[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/GetUserRoomList")]
public IActionResult GetUserRoomList([FromForm] string request) {
// TODO: Categories are not supported
UserRoomGetRequest userRoomRequest = XmlUtil.DeserializeXml<UserRoomGetRequest>(request);
ICollection<Room>? rooms = ctx.Vikings.FirstOrDefault(x => x.Id == userRoomRequest.UserID.ToString())?.Rooms;
UserRoomResponse response = new UserRoomResponse { UserRoomList = new List<UserRoom>() };
if (rooms is null)
return Ok(response);
foreach (var room in rooms) {
if (room.RoomId == "MyRoomINT" || room.RoomId == "StaticFarmItems") continue;
UserRoom ur = new UserRoom {
RoomID = room.RoomId,
CategoryID = 541, // Placeholder
CreativePoints = 0, // Placeholder
ItemID = 0,
Name = room.Name
};
response.UserRoomList.Add(ur);
}
return Ok(response);
}
[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/SetUserRoom")]
public IActionResult SetUserRoom([FromForm] string apiToken, [FromForm] string request) {
UserRoom roomRequest = XmlUtil.DeserializeXml<UserRoom>(request);
Room? room = ctx.Sessions.FirstOrDefault(e => e.ApiToken == apiToken)?.Viking?.Rooms.FirstOrDefault(x => x.RoomId == roomRequest.RoomID);
if (room is null)
return Ok(new UserItemPositionList { UserItemPosition = new UserItemPosition[0] });
room.Name = roomRequest.Name;
ctx.SaveChanges();
return Ok(new UserRoomSetResponse {
Success = true,
StatusCode = UserRoomValidationResult.Valid
});
}
[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/GetUserActivityByUserID")]
public IActionResult GetUserActivityByUserID() {
return Ok(new ArrayOfUserActivity { UserActivity = new UserActivity[0] });
}
[HttpPost]
[Produces("application/xml")]
[Route("ContentWebService.asmx/SetNextItemState")]
public IActionResult SetNextItemState([FromForm] string setNextItemStateRequest) {
SetNextItemStateRequest request = XmlUtil.DeserializeXml<SetNextItemStateRequest>(setNextItemStateRequest);
RoomItem? item = ctx.RoomItems.FirstOrDefault(x => x.Id == request.UserItemPositionID);
if (item is null)
return Ok();
// NOTE: The game sets OverrideStateCriteria only if a speedup is used
return Ok(roomService.NextItemState(item, request.OverrideStateCriteria));
}
private RaisedPetData GetRaisedPetDataFromDragon (Dragon dragon) {
RaisedPetData data = XmlUtil.DeserializeXml<RaisedPetData>(dragon.RaisedPetData);
data.RaisedPetID = dragon.Id;

View File

@ -10,6 +10,8 @@ public class Room {
public string VikingId { get; set; } = null!;
public string? Name { get; set; }
public virtual Viking? Viking { get; set; }
public virtual ICollection<RoomItem> Items { get; set; } = null!;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "ArrayOfUserActivity", Namespace = "http://api.jumpstart.com/")]
[Serializable]
public class ArrayOfUserActivity {
// Token: 0x040036D0 RID: 14032
[XmlElement(ElementName = "UserActivity")]
public UserActivity[] UserActivity;
}

View File

@ -0,0 +1,12 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[Serializable]
public class CommonInventoryConsumable {
[XmlElement(ElementName = "CIID")]
public int CommonInventoryID;
[XmlElement(ElementName = "IID")]
public int ItemID;
}

View File

@ -0,0 +1,37 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public enum ItemStateChangeError {
[XmlEnum("1")]
Success = 1,
[XmlEnum("2")]
CannotOverrideCriteria,
[XmlEnum("3")]
TimeNotReached,
[XmlEnum("4")]
CannotFindInventoryItem,
[XmlEnum("5")]
UsesLessThanRequired,
[XmlEnum("6")]
UnableToGetItem,
[XmlEnum("7")]
QtyLessThanRequired,
[XmlEnum("8")]
ItemNotInUserInventory,
[XmlEnum("9")]
TransitionFailed,
[XmlEnum("10")]
ItemStateNotInStateList,
[XmlEnum("11")]
OverrideCriteriaNotFound,
[XmlEnum("12")]
NoCriteriasFound,
[XmlEnum("13")]
AutomaticPurchaseFailed,
[XmlEnum("14")]
ItemStateExpired,
[XmlEnum("255")]
Error = 255
}

View File

@ -0,0 +1,27 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[Serializable]
public class SetNextItemStateRequest {
[XmlElement(ElementName = "SID")]
public int StoreID { get; set; }
[XmlElement(ElementName = "PGID")]
public int ProductGroupID;
[XmlElement(ElementName = "UID")]
public Guid UserID;
[XmlElement(ElementName = "CIID")]
public int UserInventoryCommonID;
[XmlElement(ElementName = "UIPID")]
public int UserItemPositionID;
[XmlElement(ElementName = "OSC")]
public bool OverrideStateCriteria;
[XmlElement(ElementName = "CICS")]
public List<CommonInventoryConsumable> CommonInventoryConsumables;
}

View File

@ -0,0 +1,18 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[Serializable]
public class SetNextItemStateResult {
[XmlElement(ElementName = "EC")]
public ItemStateChangeError ErrorCode { get; set; }
[XmlElement(ElementName = "UIS")]
public UserItemState UserItemState;
[XmlElement(ElementName = "RS")]
public AchievementReward[] Rewards;
[XmlElement(ElementName = "S")]
public bool Success;
}

View File

@ -0,0 +1,22 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "UA", Namespace = "")]
[Serializable]
public class UserActivity {
[XmlElement(ElementName = "id", IsNullable = true)]
public int? UserActivityID;
[XmlElement(ElementName = "uid", IsNullable = true)]
public Guid? UserID;
[XmlElement(ElementName = "rid", IsNullable = true)]
public Guid? RelatedUserID;
[XmlElement(ElementName = "aid", IsNullable = true)]
public int? UserActivityTypeID;
[XmlElement(ElementName = "d", IsNullable = true)]
public DateTime? LastActivityDate;
}

26
src/Schema/UserRoom.cs Normal file
View File

@ -0,0 +1,26 @@
using System.Diagnostics;
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "UR", Namespace = "")]
[Serializable]
public class UserRoom {
[XmlElement(ElementName = "N")]
public string Name;
[XmlElement(ElementName = "R")]
public string RoomID;
[XmlElement(ElementName = "CP")]
public double CreativePoints;
[XmlElement(ElementName = "C")]
public int? CategoryID;
[XmlElement(ElementName = "IID")]
public int? ItemID;
[XmlElement(ElementName = "SN")]
public string SceneName;
}

View File

@ -0,0 +1,12 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "URGR", Namespace = "")]
[Serializable]
public class UserRoomGetRequest {
[XmlElement(ElementName = "UID")]
public Guid? UserID { get; set; }
[XmlElement(ElementName = "CID")]
public int? CategoryID { get; set; }
}

View File

@ -0,0 +1,10 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "URR", Namespace = "")]
[Serializable]
public class UserRoomResponse {
// Token: 0x040037EB RID: 14315
[XmlElement(ElementName = "ur")]
public List<UserRoom> UserRoomList;
}

View File

@ -0,0 +1,13 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
[XmlRoot(ElementName = "URSR", Namespace = "", IsNullable = false)]
[Serializable]
public class UserRoomSetResponse {
[XmlElement(ElementName = "S")]
public bool Success { get; set; }
[XmlElement(ElementName = "SC")]
public UserRoomValidationResult StatusCode { get; set; }
}

View File

@ -0,0 +1,10 @@
using System.Xml.Serialization;
namespace sodoff.Schema;
public enum UserRoomValidationResult {
[XmlEnum("1")]
Valid = 1,
[XmlEnum("2")]
RMFValidationFailed
}

View File

@ -19,13 +19,13 @@ public class RoomService {
ctx.SaveChanges();
}
public int[] CreateItems(UserItemPositionSetRequest[] roomItemRequest, Room room) {
public Tuple<int[], UserItemState[]> CreateItems(UserItemPositionSetRequest[] roomItemRequest, Room room) {
List<int> ids = new();
List<UserItemState> states = new();
foreach (var itemRequest in roomItemRequest) {
// TODO: Remove item from inventory (using CommonInventoryID)
InventoryItem? i = room.Viking?.Inventory.InventoryItems.FirstOrDefault(x => x.Id == itemRequest.UserInventoryCommonID);
if (i != null) i.Quantity--;
UserItemPosition uip = itemRequest;
RoomItem roomItem = new RoomItem {
RoomItemData = XmlUtil.SerializeXml<UserItemPosition>(itemRequest).Replace(" xsi:type=\"UserItemPositionSetRequest\"", "") // NOTE: No way to avoid this hack when we're serializing a child class into a base class
};
@ -33,8 +33,22 @@ public class RoomService {
room.Items.Add(roomItem);
ctx.SaveChanges();
ids.Add(roomItem.Id);
if (itemRequest.Item.ItemStates.Count > 0) {
ItemState defaultState = itemRequest.Item.ItemStates.Find(x => x.Order == 1)!;
UserItemState userDefaultState = new UserItemState {
CommonInventoryID = (int)itemRequest.UserInventoryCommonID!,
UserItemPositionID = roomItem.Id,
ItemID = (int)itemRequest.Item.ItemID,
ItemStateID = defaultState.ItemStateID,
StateChangeDate = DateTime.Now
};
states.Add(userDefaultState);
itemRequest.UserItemState = userDefaultState;
roomItem.RoomItemData = XmlUtil.SerializeXml<UserItemPosition>(itemRequest).Replace(" xsi:type=\"UserItemPositionSetRequest\"", "");
ctx.SaveChanges();
}
}
return ids.ToArray();
return new(ids.ToArray(), states.ToArray());
}
public UserItemState[] UpdateItems(UserItemPositionSetRequest[] roomItemRequest, Room room) {
@ -84,8 +98,68 @@ public class RoomService {
foreach (var item in room.Items) {
UserItemPosition data = XmlUtil.DeserializeXml<UserItemPosition>(item.RoomItemData);
data.UserItemPositionID = item.Id;
data.ItemID = data.Item.ItemID;
itemPosition.Add(data);
}
return new UserItemPositionList { UserItemPosition = itemPosition.ToArray() };
}
public SetNextItemStateResult NextItemState(RoomItem item, bool speedup) {
SetNextItemStateResult response = new SetNextItemStateResult {
Success = true,
ErrorCode = ItemStateChangeError.Success
};
UserItemPosition pos = XmlUtil.DeserializeXml<UserItemPosition>(item.RoomItemData);
int nextStateID = GetNextStateID(pos, speedup);
DateTime stateChange = DateTime.Now;
if (nextStateID == -1) {
nextStateID = pos.UserItemState.ItemStateID;
stateChange = pos.UserItemState.StateChangeDate;
ctx.RoomItems.Remove(item);
ctx.SaveChanges();
}
response.UserItemState = new UserItemState {
CommonInventoryID = (int)pos.UserInventoryCommonID!,
UserItemPositionID = item.Id,
ItemID = pos.Item.ItemID,
ItemStateID = nextStateID,
StateChangeDate = stateChange
};
if (nextStateID != -1) {
pos.UserItemState = response.UserItemState;
item.RoomItemData = XmlUtil.SerializeXml<UserItemPosition>(pos);
ctx.SaveChanges();
}
return response;
}
private int GetNextStateID(UserItemPosition pos, bool speedup) {
if (pos.UserItemState == null)
return pos.Item.ItemStates.Find(x => x.Order == 1)!.ItemStateID;
ItemState currState = pos.Item.ItemStates.Find(x => x.ItemStateID == pos.UserItemState.ItemStateID)!;
if (speedup)
return ((ItemStateCriteriaSpeedUpItem)currState.Rule.Criterias.Find(x => x.Type == ItemStateCriteriaType.SpeedUpItem)!).EndStateID;
ItemStateCriteriaExpiry? expiry = (ItemStateCriteriaExpiry?)currState.Rule.Criterias.Find(x => x.Type == ItemStateCriteriaType.StateExpiry);
if (expiry != null) {
DateTime start = pos.UserItemState.StateChangeDate;
if (start.AddSeconds(expiry.Period) <= DateTime.Now)
return expiry.EndStateID;
}
switch (currState.Rule.CompletionAction.Transition) {
default:
return pos.Item.ItemStates.Find(x => x.Order == currState.Order + 1)!.ItemStateID;
case StateTransition.InitialState:
return pos.Item.ItemStates.Find(x => x.Order == 1)!.ItemStateID;
case StateTransition.Deletion:
return -1;
}
}
}