fomtarro / vts-sharp Goto Github PK
View Code? Open in Web Editor NEWC# Library for interacting with the VTube Studio API
Home Page: https://assetstore.unity.com/packages/tools/integration/vts-sharp-203218
License: MIT License
C# Library for interacting with the VTube Studio API
Home Page: https://assetstore.unity.com/packages/tools/integration/vts-sharp-203218
License: MIT License
The old Unity serializer had special logic to prevent it from sending certain optional values. For example, in VTSParameterInjectionValue
, the weight
field is optional. In the data model, we initialize it as NaN
. This stopped it from being included on the serialized model at all in the old way. It seems the new serializer is being overzealous.
Go through the model files and find the various sentinel values we use to try to exclude values from the payload, then update the Newtonsoft serializer to accommodate.
We can use Extension Methods to get a [Description] tag off of an Enum. This adds a little overhead, since we have to get the string at runtime as opposed to just reading the string.
Example:
using System.ComponentModel;
namespace VTS
{
public static class EnumExtensions
{
public static string GetDescription<T>(this T value)
{
var fi = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : null;
}
}
}
Pros:
Less typos, easier to develop/maintain.
Cons:
Less performant. Not sure if that is an issue, since it is all (usually) running on a local network.
VTSData
as [Serializable]
. This is marking a class being Binary Serializable in C# and is generally frowned upon for various reasons, especially since we just need to serialize it for JSON data.Pros:
Not using [Serializable].
Cons:
Adding a library (Newtonsoft) to the project.
Data
on every class. Here is an example of how VTSMessageData
and ErrorData
could lookpublic class DTO {}
public class VTSMessageData<T> where T : DTO
{
public string APIName = "VTubeStudioPublicAPI";
public long Timestamp;
public string APIVersion = "1.0";
public string RequestID = Guid.NewGuid().ToString();
public string MessageType;
public T Data;
public VTSMessageData(MessageTypeEnum e)
{
MessageType = e.GetDescription();
}
public VTSMessageData(MessageTypeEnum e, T d)
{
MessageType = e.GetDescription();
Data = d;
}
}
public class VTSErrorData : VTSMessageData<ErrorData>
{
public VTSErrorData() : base(MessageTypeEnum.APIError) { }
public VTSErrorData(string e) : base(MessageTypeEnum.APIError, new ErrorData(e)) { }
public class ErrorData : DTO
{
public ErrorData() { }
public ErrorData(string message) { Message = message; }
public ErrorID ErrorID;
public string Message;
}
}
Pros:
Code is a bit easier to work with and expand upon.
Cons:
Maybe a little less understandable at a glance?
Currently need to use Static Usings, IE: using static VTS.VTSArtMeshListRequestData;
to access the 'Data' types, which will clutter usings in some files.
I'll try to think of a better way to handle this though.
this.
, variable name casing, null coalescing, null propagation, and other VERY MINOR things. This isn't so much a problem, but thought I'd bring it up and see what your thoughts on it were. I could make these changes in a pull request, but wanted to see if this was overstepping my bounds a little.Example:
This:
if(this._ws != null){
this._ws.Update(timeDelta);
}
Becomes:
_ws?.Update(timeDelta);
Pros:
My Brain will process this better?
Cons:
Your/Other People's Brains may not?
ProcessResponses
, allowing for not having to manually update this whenever we add a new type. This example also uses (dynamic)
, which I would like to refactor out, since this also adds some extra overhead along with the use of Reflection. I just haven't thought of a better way of doing this yet.Example:
private void ProcessResponses()
{
while (_ws?.GetNextResponse() is string data)
{
var response = JsonConvert.DeserializeObject<VTSMessageData<DTO>>(data);
Type responseType = Type.GetType("VTS" + response.MessageType + "Data");
var responseData = JsonConvert.DeserializeObject(data, responseType);
try
{
if (_events.ContainsKey(response.MessageType))
{
_events[response.MessageType].OnEvent((dynamic)responseData);
}
else if (_callbacks.ContainsKey(response.RequestID))
{
if (response.MessageType == "APIError")
{
_callbacks[response.RequestID].OnError((dynamic)responseData);
}
else
{
_callbacks[response.RequestID].OnSuccess((dynamic)responseData);
}
_callbacks.Remove(response.RequestID);
}
}
catch (Exception e)
{
VTSErrorData error = new VTSErrorData(e.Message)
{
RequestID = response.RequestID
};
_events[response.MessageType].OnError(error);
}
}
}
Pros:
Easier to maintain.
Cons:
Uses more computation.
Please let me know if any of these possible enhancements interest you! I'll make some pull requests with these features separately. I already have some of this work done in one, gross, big, commit I was working on as a test over at Millyarde@e3851fb. I'll probably wait until version 2 is in main to make these changes, if that isn't too far off!
I have received a report that the socket gets rejected by the server after an hour (or much more) of connectivity. I suspect in this case, we need to put the attempted request back in the stack,
I unfortunately cannot reliably reproduce these conditions. But I will keep trying.
The GetParameterValue call's inline doc says
For more info, see /// <a href="https://github.com/DenchiSoft/VTubeStudio#requesting-list-of-available-tracking-parameters">https://github.com/DenchiSoft/VTubeStudio#requesting-list-of-available-tracking-parameters</a>
However, that's the link for GetInputParameterList. The link for GetParameterValue should probably be this:
https://github.com/DenchiSoft/VTubeStudio#get-the-value-for-one-specific-parameter-default-or-custom
I feel like it would be easier to use the library by making a nuget package, this way it could directly be automatically downloaded and setup during build
I was trying to develop something using VTS-Sharp and WebSocket Sharp. Initially, I was having problems with WebSocket Sharp classes that couldn't be found and I'd thought that maybe you'd stripped out all the stuff you didn't need from WebSocket Sharp.
So I installed WebSocket Sharp myself, from https://www.nuget.org/packages/WebSocketSharp with the NuGet command line since I didn't really want to install and try and figure out MonoDevelop just to build it from source. Unfortunately, the most recent version on NuGet is pretty old anyway, from back in July 2016, but it seemed to have the classes that I thought were missing (version 1.0.3-rc11 appears to be up to and including this commit sta/websocket-sharp@c044fdc).
Installing version 1.0.3-rc11 with NuGet and replacing the existing websocket-sharp.dll in my Unity project with that version gave a bunch of errors with the VTS-Sharp code.
I noticed in particular that WebSocketSharpImpl.cs was trying to use WebSocketState.CONNECTING, but that was changed to PascalCase in March 2014: sta/websocket-sharp@d632fc8 leading me to guess that you're using a prerelease version older than that.
It could be that the newer versions have some issues that I don't know about or that you have other reasons for using the chosen version of WebSocket Sharp, but I thought it was a bit odd so thought it was worth pointing out.
The old version isn't stopping me from developing or anything since I can look back at all the old code on github and so far it's mostly just changed class names that I've been running into, so feel free to close this issue.
ErrorID - ItemPinRequestInvalidAngleOrSizeType
it doesn't matter which combination of VTSItemAngleRelativityMode
and VTSItemSizeRelativityMode
, size or angle it'll always return this error when dynamically pinning objects.
any way you got this working?
code:
// Load the item
var item = await plugin.VTSPlugin.LoadItem(prop.fileName, new VTSCustomDataItemLoadOptions()
{
positionX = 0,
positionY = 0,
});
//get Item info
var itemInstanceId = item.data.instanceID;
// Get Current Model Info
var model = await plugin.VTSPlugin.GetCurrentModel();
// Get a list of artmeshes
var artMeshes = await plugin.VTSPlugin.GetArtMeshList();
// Get the first artmesh
var artMesh = artMeshes.data.artMeshNames.FirstOrDefault();
// Model Id and ArtMesh Id left empty for random
await plugin.VTSPlugin.PinItemToRandom(itemInstanceId, "", "", 1,
VTSItemAngleRelativityMode.RelativeToPinPosition,
1,
VTSItemSizeRelativityMode.RelativeToCurrentItemSize); // Lots of expermentation
In the VTSWebSocket.cs, the ProcessResponses method is missing a case for ParameterValueResponse.
Currently, the plugin will attempt to get a new auth token on every run, which works, but is annoying. It would probably be preferential to have a mechanism in place for being able to provide a prior token into the Initialize
method.
In VTSWebSocket in the function StartUDP() at line 70, the udp client is being initialized with:
UDP_CLIENT = new UdpClient(47779);
Like this it will take full ownership of the port. To allow sharing of the port it must instead be created with something like:
IPEndPoint LOCAL_PT = new IPEndPoint(IPAddress.Any, 47779);
UDP_CLIENT = new UdpClient();
UDP_CLIENT.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
UDP_CLIENT.Client.Bind(LOCAL_PT);
this sets the socket options to allow reuse
It can be quite hard to find the right function that you want at the moment, I thought maybe a documentation Website(gh-pages) or github wiki could be made. I don't know which one you prefer, I can probably help out with that!
As the title says, the way the default websocket implementation was written can cause an "unassigned reference" issue with later versions of Unity/.NET on the send message, which is only assigned inside a try/catch block.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.