Unity Forge Networking: client's score doesn't get saved - unity3d

I am having an issue with forge networking.I have finished beginner tutorials and now I wanted to create my own project.It is very simple:I create a local game on one of my devices and client connects.Each of them(client and host) enter their number, and then host's number is displayed to client and vice versa.However, client's number never gets saved and host always sees 0 for client's number, while the actual number is different(the host's number is getting saved and synchronized through the network properly and client CAN read it). I took these steps in creating this project: 1) in forge network contract wizard I have created 2 variables for storing numbers 2) i created a simple script for saving and loading those 2 numbers(this script is attached to an empty game object) :
public void UpdateNumber(Text Nmbr)//Button!
{
if (!networkObject.IsServer) {
networkObject.clientNmbr = int.Parse (Nmbr.text);
} else {
networkObject.hostNmbr = int.Parse (Nmbr.text);
}
}
public void ShowText(Text text)
{
if (networkObject.IsServer ) {
text.text = "client:" + networkObject.clientNmbr;
} else {
text.text = "host:" + networkObject.hostNmbr;
}
}
I don't see why this works for host but doesn't work for the client

Related

How can I handle high traffic increase on my google cloud feature?

My situation is the following:
I developed a game, where people can save their progress via google cloud. I'm releasing big content-updates, resulting in many people returning to my game at the same time, trying to get their savegame.
This overload causing my customers to get stucked in the savegame-loading process - not able to start playing with their progress.
Here's an updated 4 days screenshot of the cloud-api-dashboard
(And here's the old "12 hours" screenshot of the cloud-api-dashboard)
more informations about the Project:
The game keeps using the "save in cloud"-function in the background on some stages of the game to provide players with the functionality to play on two diffrent devices.
I'm using Unity 2019.3.9f1 and the Asset "Easy Mobile Pro 2.17.3" for the Game-Service-Feature.
The "Google Play Games Plugin" has the version "0.10.12" and can be found on github
more informations about the Cloud-Situation:
The OAuth "user type" is "External" (and can't be changed)
The OAuth user cap display shows "0/100" for the user-cap
And The OAuth rate limits is displaying this for the token-grant-rate (highest "Average Token Grant Rate" is 3,33 of 10.000 as limit)
All used quotas are within the limit. The project reaches
1/10 of "queries per day" (1.000.000.000 max) and
1/2 of "queries per 100 sec" (20.000 max).
more informations about the Error-Trace in the cloud-API:
On my search for a better Error-Log I tried to find “Cloud Logging”-tools in the “Google Cloud Platform”-console. But every section i tried won’t display anything:
“Logging” (Operations tool) is empty
“Cloud Logging API” says: “no data available for the selected time frame.”
“Cloud Debugger API” says: “no data available for the selected time frame.”
I can't find a more detailed variant of the errors as this (the "Metrics"-Section in the "Google Drive API"):
Is there anything I miss to get a better insight?
more informations about the Core-Code
As I mentioned, I’m using “EasyMobilePro”, so I have one “SaveGame”-Var and 8 calls for google and apple as well. I already contacted their support: They assured me that those calls are unchangeable & kind of rock solid (so it can’t be caused from their code) and I should try to contact google if the problem is not on my side.
The 5 calls from EasyMobile for cloudsave are:
bool “GameServices.IsInitialized()”
void “GameServices.OpenWithAutomaticConflictResolution”
void “GameServices.WriteSavedGameData”
void “GameServices.ReadSavedGameData”
void “GameServices.DeleteSavedGame”
The 3 calls from EasyMobile for cloud-login are:
void “GameServices.Init()”
delegate “GameServices.UserLoginSucceeded”
delegate “GameServices.UserLoginFailed”
The Process, that causes the Issue:
I call “GameService.Init()”, the user logs in (no problem)
On that “LoginSuccess”-Callback I call my Function “HandleFirstCloudOpening”:
//This Method is Called, after the player Pressed "Save/ Load" on the StartScreen
//The button is disabled imidiately (and will be re-enabled if an error/fail happens)
public void TryCallUserLogin() {
if (!IsLoginInit) {
EasyMobile.GameServices.UserLoginFailed += HandleLoginFail;
EasyMobile.GameServices.UserLoginSucceeded += HandleFirstCloudOpening;
IsLoginInit = true;
}
if (!IsGameServiceInitialized) {
EasyMobile.GameServices.Init();
} else { //This "else" is only be called, if the "Init" was successfull, but the player don't have a connected savegame
HandleFirstCloudOpening();
}
}
private void HandleLoginFail() {
//(...) Show ErrorPopup, let the player try to login again
}
private void HandleFirstCloudOpening() {
if (currentSaveState != CloudSaveState.NONE) {
CloudStateConflictDebug(CloudSaveState.OPENING);
return;
}
currentSaveState = CloudSaveState.OPENING;
EasyMobile.GameServices.SavedGames.OpenWithAutomaticConflictResolution(cloudSaveNameReference, UseFirstTimeOpenedSavedGame);
}
private void UseFirstTimeOpenedSavedGame(EasyMobile.SavedGame _savedGame, string _error) {
currentSaveState = CloudSaveState.NONE;
if (string.IsNullOrEmpty(_error)) {
cloudSaveGame = _savedGame;
ReadDataFromCloud(cloudSaveGame);
} else {
ErrorPopupWithCloseButton("cloud_open", "failed with error: " + _error);
}
}
private void ReadDataFromCloud(EasyMobile.SavedGame _savedGame) {
if (_savedGame.IsOpen) {
currentSaveState = CloudSaveState.LOADING;
EasyMobile.GameServices.SavedGames.ReadSavedGameData(_savedGame, UseSucessfullLoadedCloudSaveGame);
} else { //backup function if the fresh-opened savegame is "closed" for some reason (can happen later while "saving" ingame)
HandleFirstCloudOpening();
}
}
private void UseSucessfullLoadedCloudSaveGame(EasyMobile.SavedGame _game, byte[] _cloudData, string error) {
if (!string.IsNullOrEmpty(error)) {
ErrorPopupWithCloseButton("cloud_read", "Reading saved game data failed: " + error);
return;
}
if (_cloudData.Length > 0) {
//A function, that converts the saved bytes to my useable Savegame-Data
//having a "try&catch": if it fails, it useses the callback with the param "null"
SaveGameToByteConverter.LoadFromBytes<CoreSaveData>(_cloudData, UseSucessfullConvertedSavegameData);
} else {
//this will "fail", causing the use of the callback with the param "null"
SaveGameToByteConverter.LoadFromBytes<CoreSaveData>(null, UseSucessfullConvertedSavegameData);
}
}
private void UseSucessfullConvertedSavegameData(CoreSaveData _convertedSaveGame) {
//Has a Loaded & normal SaveGame in his cloud
if (_convertedSaveGame != null) {
//Loaded Save matches verify-conditions
if (CheckLoadedSaveIsVerified(_convertedSaveGame)) {
OverrideGameSaveDatawithLoaded(_convertedSaveGame);
ReloadCurrentScene();
return;
} else { //This happens if the cloud-save doesn't pass my verification-process
ErrorPopupWithCloseButton("cloud_loadedSave", "Couldn't find a compatible Savegame!");
return;
}
} else { //User uses Cloud-save for the frist Time or has an unusable savegame and gets a "new" (lost his old data)
TrySaveGameToCloud((bool _saved) => {
SaveAllGameFilesLocally();
});
}
}
I shrunk the code by removing most of my “if error happens, do XY”, since there are many and they would extend the reprex. If necessary I can provide a more detailed (but more complicated) code.
current conclusion
I can't find any issue on my side, that wouldn't have been fixed with a "restart of the game" or woudln't been covered by an error-popup for the user. It's like they are queued because of the amount of users and need to wait way too long for a response. Some users told us they had to wait & tried "x hours" (it's variable from 2h to 36h) and then they passed to play with their progress (so it worked). But some players mentioned they couldn't play again on the next day (same problem). Like their "access-token" only holds for a day?
Edit-History:
(1) updated the first dash-board-picture to match the ongoing situation
(1) added "more informations about the cloud-situation"
(1) can't find a more detailed error-log
(2) removed most pictures as displayables (kept the links)
(2) added "more informations about the Error-Trace in the cloud-API"
(2) added "more informations about the Core-Code" and a Reprex
(2) added "current conclusion"

Netty Client Connect with Server, but server does not fire channelActive/Registered

I have the following architecture in use:
- [Client] - The enduser connecting to our service.
- [GameServer] - The game server on which the game is running.
- [GameLobby] - A server that is responsible for matching Clients with a GameServer.
If we have for example 4 Clients that want to play a game and get matched to a GameLobby, then the first time all these connection succeeds properly.
However when they decide to rematch, then one of the Clients will not properly connect.
The connection between all the Clients and the GameServer happens simultaneously.
Clients that rematch first removes their current connection with the GameServer and head into the lobby again.
This connection will succeed, no errors are thrown. Even using a ChannelFuture it shows that the client connection was made properly, the following values are retrieved to show that the client thinks the connection was correct:
- ChannelFuture.isSuccess() = True
- ChannelFuture.isDone() = True
- ChannelFuture.cause() = Null
- ChannelFuture.isCancelled() = False
- Channel.isOpen() = True
- Channel.isActive() = True
- Channel.isRegistered() = True
- Channel.isWritable() = True
Thus the connection was properly made according to the Client. However on the GameServer at the SimpleChannelInboundHandler, the method ChannelRegistered/ChannelActive is never called for that specific Client. Only for the other 3 Clients.
All the 4 Clients, the GameServer, and the Lobby are running on the same IPAddress.
Since it only happens when (re)connecting again to the GameServer, I thought that is had to do with not properly closing the connection. Currently this is done through:
try {
group.shutdownGracefully();
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
On the GameServer the ChannelUnregister is called thus this is working, and the connection is destroyed.
I have tried adding listeners to the ChannelFuture of the malfunctioning channel connection, however according to the channelFuture everything works, which is not the case.
I tried adding ChannelOptions to allow for more Clients queued to the server.
GameServer
The GameServer server is initialized as follow:
// Create the bootstrap to make this act like a server.
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitialisation(new ClientInputReader(gameThread)))
.option(ChannelOption.SO_BACKLOG, 1000)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true);
bossGroup.execute(gameThread); // Executing the thread that handles all games on this GameServer.
// Launch the server with the specific port.
serverBootstrap.bind(port).sync();
The GameServer ClientInputReader
#ChannelHandler.Sharable
public class ClientInputReader extends SimpleChannelInboundHandler<Packet> {
private ServerMainThread serverMainThread;
public ClientInputReader(ServerMainThread serverMainThread) {
this.serverMainThread = serverMainThread;
}
#Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("[Connection: " + ctx.channel().id() + "] Channel registered");
super.channelRegistered(ctx);
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, Packet packet) {
// Packet handling
}
}
The malfunction connection is not calling anything of the SimpleChannelInboundHandler. Not even ExceptionCaught.
The GameServer ChannelInitialisation
public class ChannelInitialisation extends ChannelInitializer<SocketChannel> {
private SimpleChannelInboundHandler channelInputReader;
public ChannelInitialisation(SimpleChannelInboundHandler channelInputReader) {
this.channelInputReader = channelInputReader;
}
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// every packet is prefixed with the amount of bytes that will follow
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new PacketEncoder(), new PacketDecoder(), channelInputReader);
}
}
Client
Client creating a GameServer connection:
// Configure the client.
group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitialisation(channelHandler));
// Start the client.
channel = b.connect(address, port).await().channel();
/* At this point, the client thinks that the connection was succesfully, as the channel is active, open, registered and writable...*/
ClientInitialisation:
public class ChannelInitialisation extends ChannelInitializer<SocketChannel> {
private SimpleChannelInboundHandler<Packet> channelHandler;
ChannelInitialisation(SimpleChannelInboundHandler<Packet> channelHandler) {
this.channelHandler = channelHandler;
}
#Override
public void initChannel(SocketChannel ch) throws Exception {
// prefix messages by the length
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
ch.pipeline().addLast(new LengthFieldPrepender(4));
// our encoder, decoder and handler
ch.pipeline().addLast(new PacketEncoder(), new PacketDecoder(), channelHandler);
}
}
ClientHandler:
public class ClientPacketHandler extends SimpleChannelInboundHandler<Packet> {
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
System.out.println("Channel active: " + ctx.channel().id());
ctx.channel().writeAndFlush(new PacketSetupClientToGameServer());
System.out.println("Sending setup packet to the GameServer: " + ctx.channel().id());
// This is successfully called, as the client thinks the connection was properly made.
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, Packet packet) {
// Reading packets.
}
}
I expect that the Client could connect properly to the server. Since the other Clients are properly connecting and the client could previously connect just fine.
TL;DR: When multiple Clients try to create a new match, there is a possibility that one, possibly more, Client(s) will not connect properly with the server, after the previous connection was closed.
For some that struggle with this issue in some way or another.
I did a workaround that allows me to continue even tho there is still a bug inside the Netty framework (as far as I am concerned). The workaround is quite simple just create a connection pool.
My solution uses a maximum of five connections inside the connection pool. If one of the connection gets no reply from the GameServer, then it is not that big of a deal, since there are four others that will have a high chance of succeeding. I know this is a bad workaround, but I could not find any information on this issue. It works and only gives a maximum delay of 5 seconds (each retry takes a second)

How to set up and use the Kin blockchain in a Unity app - Step by step

What is a good step by step explanation on how to use the Kin Unity SDK in an empty project in Unity?
Option 1: Use a pre-made wrapper
Use the wrapper in this 10 minute set up code (client and server) and call it as follows:
kinWrapper.SendPayment(1.00M, "test send", address);
kinWrapper.Balance();
//etc
Here is a detailed tutorial on implmenting the wrapper.
Option 2: Create your own wrapper
The Kin blockchain is a fork of the stellar protocol. As such, operations in your app will boil down to the following:
Creating accounts on the blockchain
Funding accounts on the blockchain
Sending payments
You can clone a sample implementation here - or you can follow the steps below to get a basic understanding.
Installation
Create an empty Unity project, and install the Kin Unity Plugin and modify your gradle file as described here.
NOTE: The SDK only runs on an Android/iOS device or emulator. The SDK will not run in the unity editor or unity remote -so you will need to compile and run the code in an emulator or device.
Implementation
Create an empty MonoBehaviour script - name it KinWrapper, and declare the Kin namespace:
using Kin;
You can also enable blockchain listeners by adding the following to your MonoBehaviour :
public class KinWrapper : MonoBehaviour, IPaymentListener, IBalanceListener
Instantiating
Next declare instances of the Kin Client and Kin Account
private KinClient kinClient;
private KinAccount kinAccount;
The client handles the native code depending on the platform you are using. (Android or iOS). The account is the main interface you will be using.
Creating an account
Before using the account, you first have to instantiate it through the client.
kinClient = new KinClient(Kin.Environment.Test, "appId");
if (!kinClient.HasAccount())
{
kinAccount = kinClient.AddAccount();
}else{
kinAccount = kinClient.GetAccount(0);
}
The code above first checks to see if a local account exists on the device. If it doesn't, it creates an account on the device and returns that as a reference.
NOTE: A 'created account' is simply a public key and private key generated on the device. These local keys enable the device to communicate with the blockchain.
You can now get the public key (client's blockchain address) by calling:
kinAccount.GetPublicAddress();
Onboarding/ Funding an account
Because this key only exists locally, you need to 'on-board' it to the blockchain. In other words, a special function needs to be called, to fund and register it online. Before this step is done, the address is considered invalid on the blockchain, and cannot receive or make transactions. This is part of the stellar protocol to avoid spam and unnecessary account creation.
You can check if the account has been on-boarded and funded by calling the following function:
kinAccount.GetStatus(GetStatusCallback)
void GetStatusCallback(KinException ex, AccountStatus status)
{
if (status == AccountStatus.Created)
{
}
else
{
}
}
To on-board and fund an account on the test blockchain, you have 3 options:
Option 1: You can do this manually by pasting the address on Kin's friend-bot service. This will on-board the account for you automatically.
Option 2: You can call the friend-bot through your code as an http request
https://friendbot-testnet.kininfrastructure.com/?addr=address_here
public static IEnumerator FundAccount( string publicAddress, Action<bool> onComplete = null )
{
var url = "http://friendbot-testnet.kininfrastructure.com/?addr=" + publicAddress;
var req = UnityWebRequest.Get( url );
yield return req.SendWebRequest();
if( req.isNetworkError || req.isHttpError )
{
Debug.Log( req.error );
if( onComplete != null )
onComplete( false );
}
else
{
Debug.Log( "response code: " + req.responseCode );
Debug.Log( req.downloadHandler.text );
if( onComplete != null )
onComplete( true );
}
}
Option 3: You can use server side code to fund it yourself (which you must do in a production environment). Basically, your client will need to call your server and request to be on-boarded. Your server can then use Kin's python SDK or node SDK to perform the on-boarding.
Here is a handy python implementation or Node.js implementation you can use for your server.
Calling the implementation from Unity is as simple as follows:
public IEnumerator FundMe(decimal amount, Action<bool> fundCallback = null)
{
WWWForm form = new WWWForm();
form.AddField("address", kinAccount.GetPublicAddress());
form.AddField("amount", amount.ToString());
reqUrl = "http://address.to.your.server";
var req = UnityWebRequest.Post(reqUrl, form);
yield return req.SendWebRequest();
if (req.isNetworkError || req.isHttpError)
{
Debug.LogError(req.error);
fundCallback(false);
}
else
{
fundCallback(true);
}
}
NOTE: Kin has two blockchains. Production and Test. The test blockchain merely has "play currency" that you can run tests on without incurring costs. The production blockchain has real world value Kin
Calling Other Functions
GetBalance()
account.GetBalance(GetBalanceCallback);
void GetBalanceCallback(KinException ex, decimal balance)
{
if (ex == null)
{
Debug.Log( "Balance: " + balance );
}
else
{
Debug.LogError( "Get Balance Failed. " + ex );
}
}
DeleteAccount()
kinClient.DeleteAccount(0);
NOTE: GetBalance() needs a callback because it's communicating with the blockchain online. DeleteAccount() doesn't because it simply deletes the private and public keys on the client. Once deleted, these keys can not be recovered.
Handling transactions
Transactions have the following parameters:
Memo: an optional string with extra information.
Address: the address of the recipient
Amount: amount of Kin you are sending (excluding fees)
Fee: the fee you will pay for your transaction to be processed by the blockchain. The current fee is 100/100000 KIN. This fee protects the blockchain from spam.
NOTE: The fee is denominated in the smallest unit of Kin. (Quark). Just like the smallest unit of a dollar is a cent. So when calling a transaction, set the fee to 100 (quarks) - which is 100/100000 KIN. Setting the fee tells the blockchain you are explicitly agreeing to pay the transaction fee. If you set the fee too low, your transaction will be rejected.
You can view all transactions on Kin's blockchain at: https://laboratory.kin.org/index.html#explorer?resource=payments
To send a payment, first build the transaction locally :
kinAccount.BuildTransaction(address, amount, fee, memo, BuildTransactionCallBack);
Building a transaction performs several operations on the client to authorise it and sign it. After the transaction is built, use the following callback to send it to Kin's blockchain.
void BuildTransactionCallBack(KinException ex, Transaction transaction)
{
if (ex == null)
{
kinAccount.SendTransaction(transaction, SendTransactionCallback);
}
else
{
Debug.LogError("Build Transaction Failed. " + ex);
}
}
Once you have sent the transaction to Kin's blockchain, you can listen for the response, to make sure it went through correctly. A transaction typically takes less than 10 seconds to complete.
void SendTransactionCallback(KinException ex, String transactionId)
{
if (ex == null)
{
//Success
}
else
{
Debug.LogError("Send Transaction Failed. " + ex);
}
}
NOTE: You should use co-routines for functions that need to wait. This will prevent your code from hanging while waiting for responses, and will create a better user experience.
Sending transactions with zero fees
You can send transactions with zero fees by being whitelisted by the Kin Foundation. To get whitelisted, simply register with the Kin Developer Program. Once approved, you can perform an extra step to let your client send transactions with zero fees.
To send a zero-fee transaction after building the transaction:
The client will need to contact your whitelisted server.
Your server will approve/ sign the transaction and send it back to the client.
The client will then send this approved transaction to Kin's blockchain, and it will be processed for free.
We have already built the transaction, now we whitelist it
void BuildTransactionCallBack(KinException ex, Transaction transaction)
{
if (ex == null)
{
StartCoroutine(WhitelistTransaction(transaction, WhitelistTransactionCallback))
}
else
{
Debug.LogError("Build Transaction Failed. " + ex);
}
}
We are calling our whitelisted server to authorise the transaction
IEnumerator WhitelistTransaction(Transaction transaction, Action<string, string> onComplete)
{
var postDataObj = new WhitelistPostData(transaction);
var postData = JsonUtility.ToJson(postDataObj);
var rawPostData = Encoding.UTF8.GetBytes(postData);
// UnityWebRequest does not work correclty when posting a JSON string so we use a byte[] and a hacky workaround
var req = UnityWebRequest.Post(baseURL + whitelistURL, "POST");
req.SetRequestHeader("Content-Type", "application/json");
req.uploadHandler = new UploadHandlerRaw(rawPostData);
yield return req.SendWebRequest();
if (req.isNetworkError || req.isHttpError)
{
Debug.LogError(req.error);
onComplete(null, null);
}
else
{
onComplete(req.downloadHandler.text, transaction.Id);
}
}
Our server has approved the transaction and sent back a signed string. We now send this signed string to Kin's blockchain
void WhitelistTransactionCallback(string whitelistTransaction, string transactionId)
{
if (whitelistTransaction != null)
{
kinAccount.SendWhitelistTransaction(transactionId, whitelistTransaction, SendTransactionCallback);
}
else
{
Debug.LogError("Whitelisting Transaction Failed. ");
}
}
As usual, we wait to see if the transaction was successful
void SendTransactionCallback(KinException ex, String transactionId)
{
if (ex == null)
{
//Success
}
else
{
Debug.LogError("Send Transaction Failed. " + ex);
}
}
Before your server has been approved for whitelisting, you can use this pre-approved server in the TEST environment. http://34.239.111.38:3000/whitelist.
public static IEnumerator WhitelistTransaction( Transaction transaction, Action<string> onComplete = null )
{
var postDataObj = new WhitelistPostData( transaction );
var postData = JsonUtility.ToJson( postDataObj );
var rawPostData = Encoding.UTF8.GetBytes( postData );
// UnityWebRequest does not work correclty when posting a JSON string so we use a byte[] and a hacky workaround
var url = "http://34.239.111.38:3000/whitelist";
var req = UnityWebRequest.Post( url, "POST" );
req.SetRequestHeader( "Content-Type", "application/json" );
req.uploadHandler = new UploadHandlerRaw( rawPostData );
yield return req.SendWebRequest();
if( req.isNetworkError || req.isHttpError )
{
Debug.Log( req.error );
if( onComplete != null )
onComplete( null );
}
else
{
Debug.Log( "response code: " + req.responseCode );
Debug.Log( req.downloadHandler.text );
if( onComplete != null )
onComplete( req.downloadHandler.text );
}
}
Once approved, you can use the sample python implementation or Node.js implementation to approve transactions on your server. You can also use the node.js SDK.
Listening to events on the blockchain
You can add listeners to prevent your client from over-querying the blockchain. Simply add the following code
kinAccount.AddPaymentListener(this);
kinAccount.AddBalanceListener(this);
public void OnEvent(PaymentInfo data)
{
//Listening for incoming and outgoing payments
}
public void OnEvent(decimal balance)
{
//Listening for changes in client's balance
}
Putting it all together
You can find this code implemented on: https://github.com/hitwill/kin-sdk-unity-tutorial under the MIT license.
Developer Playbook
You can also check this Developer Playbook - for some higher level concepts of creating with Kin.
Best practices
UX
(placeholder)
Server side security
Here is a great writeup on server side security.
Client side security
(placeholder)
Reducing calls to the blockchain
(placeholder)

Write to HID with Chip Selection with .NET Console App

Hi I am writing a simple console app that needs to write bytes to MCP2210 USB to SPI Master
I found this library over here, seems to do good job with connecting the device and reading the metadata.
I am writing message to the board as below
public static byte[] Talk()
{
var device = DeviceList.Local.GetHidDevices(1240, 222).FirstOrDefault();
if (device == null)
{
Console.WriteLine($"Could not find a device with Vendor Id:1240, Product Id:222 ");
return null;
}
var reportDescriptor = device.GetReportDescriptor();
foreach (var deviceItem in reportDescriptor.DeviceItems)
{
Console.WriteLine("Opening device for 20 seconds...");
if (!device.TryOpen(out var hidStream))
{
Console.WriteLine("Failed to open device.");
continue;
}
Console.WriteLine("Opened device.");
hidStream.ReadTimeout = Timeout.Infinite;
hidStream.Write(new byte[3] {60, 00, 00});
}
Not sure If I am writing it correctly.
While writing I need to do a chip selection as displayed in this other terminal
Any help is greatly appreciated
Here is the MC I am using https://www.microchip.com/wwwproducts/en/MCP2210
I do not see a closing of your stream. This may cause your data to not even being sent (at least not in time).
Consider using blocks with streams.
But with out parameters not possible.

Problems when using robocode with JESS

I'm attempting to use JESS in order to utilise a rule-based system for making a robot. I've got both robocode and the JESS .jar imported into Eclipse. Here's my code -
public class myRobot extends Robot {
Rete r = new Rete();
public void run() {
try {
String reset = "(reset)";
r.executeCommand(reset);
String enemyInfo = "(deftemplate enemyInfo (slot present) (slot energy) (slot name))";
r.executeCommand(enemyInfo);
while (true) {
String command = "(assert (enemyInfo (present no) (energy -1) (name none)))";
r.executeCommand(command);
}
} catch (JessException ex) {
System.err.println(ex);
}
}
public void onScannedRobot(ScannedRobotEvent e) {
try {
String command = "(assert (enemyInfo (present yes) (energy " + e.getEnergy() + ") (name " + e.getName() + ")))";
r.executeCommand(command);
} catch (JessException ex) {
System.err.println(ex);
}
}
}
I haven't added in any rules yet because I just wanted to check that robocode and JESS run properly together. When I fire this up, the robocode application opens up. But when I try adding this robot in a battle and starting it, the application just freezes up completely.
I can't access the robot's console to see what's wrong since it hangs just immediately after I try and start a battle. Since all my System.out.println() debugging statements are printed to the robot's console as opposed to the main one, I can't even figure out what's wrong. Any suggestions to get this to work?
The problem is, that the robocode application does not know the JESS library. You will need to include it in the jar of your exported robot.
How to include librarys to jars