I am trying to make multiplayer game via Unity. I use sample asset to try and I get an error which is in below:
InvalidOperationException: Cannot override system-specified headers
UnityEngine.Networking.UnityWebRequest.SetRequestHeader (System.String name, System.String value) (at C:/buildslave/unity/build/artifacts/generated/common/modules/UnityWebRequest/WebRequestBindings.gen.cs:482)
UnityEngine.WWW..ctor (System.String url, System.Byte[] postData, System.Collections.Generic.Dictionary`2 headers) (at C:/buildslave/unity/build/Runtime/WebRequestWWW/UWRWWW.cs:62)
QuizMaker.Administrator.AdminAPI+<api_call>c__Iterator0`1[QuizMaker.Administrator.CheckConnectionResponse].MoveNext () (at Assets/QuizMaker/Scripts/Administrator/AdminAPI.cs:77)
UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Coroutines.cs:17)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
QuizMaker.Administrator.AdminAPI:CheckConnection(Callback`1) (at Assets/QuizMaker/Scripts/Administrator/AdminAPI.cs:227)
QuizMaker.Administrator.ServerSettingsUI:_updateServerStatus() (at Assets/QuizMaker/Scripts/Administrator/Actions/ServerSettingsUI.cs:147)
QuizMaker.Administrator.ServerSettingsUI:UpdateServerStatus() (at Assets/QuizMaker/Scripts/Administrator/Actions/ServerSettingsUI.cs:139)
QuizMaker.Administrator.ServerSettingsUI:Start() (at Assets/QuizMaker/Scripts/Administrator/Actions/ServerSettingsUI.cs:44)
My code:
// create a form for a post data
WWWForm form = new WWWForm();
// create a log string
var logString = string.Format("[API_REQ {0}]", action);
// add key val if data is empty
if (data.Length == 0)
{
data = new string[] { "key", "val" };
}
// add data from an data array to the form
for (int i = 0; i < data.Length - 1; i += 2)
{
// add key and value
form.AddField(data[i], data[i + 1]);
// add log
logString += string.Format(" [{0}: {1}]", data[i], data[i + 1]);
}
// it needs to be more secure
var date = DateTime.Now.ToString();
var headers = form.headers;
headers["Date"] = date;
headers["Order"] = Util.generateOrderString(data);
form.AddField("hash", Util.generateHash(data, date));
// print log string
print(logString);
// create www request
var www = new WWW(AppConfig.Instance.serverUrl + action, form.data, headers);
// wait for a response
yield return www;
// print log for each api call
print(string.Format("[API_RES {0}] [{1}]", action, www.text));
// parse a response
parseResponse(
string.IsNullOrEmpty(www.error),
www.text,
callback
);
When I delete date variable, error fixes however in this time I can't connect to server. My connection method is REST API. Thanks in advance.
I found the error. If anyone encountered same issue just change the following variable:
headers["Date"] = date;
to
headers["date"] = date;
This fixed my problem and saved my hours. Best regards.
Related
I'm working on a blackjack game in Unity. I have a coroutine called InitialDeal() which handles the flow when a player clicks a BET button. It should fetch a random number from the backend API and display the dealing of the cards one by one.
Because fetching data from the API must use UnityWebRequest because the game is browser based, it is done in another coroutine called UnityApiCall.
So basically I start coroutine A (InitialDeal()) in which I start coroutine B (UnityWebRequest), and I need that every time I get the response from API, I stop the coroutine A for a number of seconds while the card is dealt.
This is what I tried. This is coroutine B that calls my API:
private IEnumerator UnityApiCall(String method, String path, String pathParams, String body, Action<JObject> result)
{
Debug.Log("In UnityApiCall...");
using UnityWebRequest www = new UnityWebRequest(apiUrl + path);
www.method = method;
if(www.method == "POST") {
byte[] byteArray = Encoding.UTF8.GetBytes(body);
UploadHandlerRaw uH = new UploadHandlerRaw(byteArray);
www.uploadHandler = uH;
}
www.downloadHandler = new DownloadHandlerBuffer();
www.SetRequestHeader("Session-ID", sessionId);
yield return www.SendWebRequest();
if (www.result == UnityWebRequest.Result.ConnectionError)
{
Debug.Log(www.error);
if (result != null)
result(new JObject("{error: " + www.error + "}"));
}
else
{
Debug.Log(www.method + " successful!");
String str = System.Text.Encoding.Default.GetString(www.downloadHandler.data);
JObject jsonResponse = JObject.Parse(str);
if (result != null)
result(jsonResponse);
}
}
This is the coroutine A:
private IEnumerator InitialDeal()
{
// initial deal
ChangeGameState(GameState.OnDealing);
int betAmount = (int) ChipManager._Instance.stacks[(int) StackType.Standard].GetValue();
Debug.Log("Bet value: " + betAmount);
// int betAmount = 10;
BetRequest betRequest = new BetRequest();
betRequest.amount = betAmount;
string pathParams = "";
string body = JsonConvert.SerializeObject(betRequest);
yield return UnityApiCall("POST", "/bet", pathParams, body, (JObject response) =>
{
Debug.Log("response: ");
Debug.Log(response);
for (int i = 0; i < 2; i++)
{
// CardData cardData = new CardData(CardData.Suit.Hearts, CardData.Rank.Four);
// CardData cardData2 = new CardData(CardData.Suit.Spades, CardData.Rank.Five);
var playerNum = response["data"]["playerCards"]["0"]["cards"][i]["number"];
var playerSymbol = response["data"]["playerCards"]["0"]["cards"][i]["symbol"]["symbol"];
var dealerNum = response["data"]["dealerCards"][i]["number"];
var dealerSymbol = response["data"]["dealerCards"][i]["symbol"]["symbol"];
var playerCard = MapCard(playerNum.ToObject<int>(), playerSymbol.ToObject<string>());
var dealerCard = MapCard(dealerNum.ToObject<int>(), dealerSymbol.ToObject<string>());
DealQueue.DealCard(player.DealCard(playerCard));
yield return new WaitForSeconds(DEAL_WAIT_TIME);
if (i == 0)
{
DealQueue.DealCard(dealer.DealCard(dealerCard, FlipType.FlipDown));
}
else
{
DealQueue.DealCard(dealer.DealCard(dealerCard, FlipType.FlipUp));
}
yield return new WaitForSeconds(DEAL_WAIT_TIME);
}
ChangeGameState(GameState.OnPlay);
});
}
This is how I tried to stop the execution for a few seconds (in the callback of UnityApiCall in the snippet above):
yield return new WaitForSeconds(DEAL_WAIT_TIME);
However, this doesn't work since it's in the callback function, and I cannot use yield there.
This is how I called the coroutine A:
StartCoroutine(InitialDeal());
You are right in the way you wait for the UnityApiCall. The callback does get the JObject but it is only local to the callback. Instead, assign it to another JObject that is method scope:
JObject jObject = null;
yield return UnityApiCall("POST", "/bet", pathParams, body,
(response) => jObject = response);
// You can use the jObject
Debug.Log("Response " + jObject );
// Your code here processing jObject
// You are in the coroutine so you can yield all you need
yield return new WaitForSeconds(DEAL_WAIT_TIME);
ChangeGameState(GameState.OnPlay);
I'm new to Unity (and c#, along with PHP) and have been tasked with getting some old c# and PHP code working. The code below is supposed to send the dictionary (formData) to the PHP server that then converts it to json. The code is below:
...
//This code runs for each file that is uploaded, the file is a list of strings and integers.
Dictionary<string, string> formData = new Dictionary<string, string>();
using (StreamReader sr = file.OpenText())
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
string[] data = s.Split(';');
uploadResultText.setText(file.Name + ":" + data[0] + " " + data[0]);
if (data[1] == "") data[1] = " ";
formData[data[0]] = data[1];
}
}
UnityWebRequest uploadRequest = UnityWebRequest.Post(serverBaseURL, formData);
currentUploadRequest = uploadRequest;
yield return uploadRequest.SendWebRequest();
...
If this code is working, how will I need to receive it server-side?
It turns out it was a server side error, the code above should work.
The server was returning http error 500 because it was requesting data that did not exist (caused by switching names on the app side, but not the server side).
The server side code uses $_POST to reference the incoming data, and a sample can be seen below.
if (isSet($_POST["App"])) {
$dataArray = array();
foreach($expectedFormInputCommon as $input) {
if (isSet($_POST[$input])) {
if (seralizeString($_POST[$input]) !== false) {
$dataArray[$input] = seralizeString($_POST[$input]);
} else {
http_response_code(400);
exit;
}
} else {
http_response_code(400);
$_POST[$input] = "empty";
exit;
}
}
}
After you Yield Return you can access the result:
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
// Error
}
else
{
var result = UnityWebRequest.Result;
}
I want to generate a shorten dynamic link from a long dynamic link
I followed the steps shown here: https://firebase.google.com/docs/dynamic-links/unity/create
yet I get the same long URL when shortening process is completed.
public void CreateInviteLink()
{
var components = new Firebase.DynamicLinks.DynamicLinkComponents(
// The base Link.
new System.Uri("https://myapp.com"),
// The dynamic link URI prefix.
"https://myapp.page.link")
{
IOSParameters = new Firebase.DynamicLinks.IOSParameters("com.myapp.myapp"),
AndroidParameters = new Firebase.DynamicLinks.AndroidParameters("com.myapp.myApp"),
};
Debug.Log("Long Dynamic Link: " + components.LongDynamicLink);
var options = new Firebase.DynamicLinks.DynamicLinkOptions
{
PathLength = DynamicLinkPathLength.Unguessable
};
Firebase.DynamicLinks.DynamicLinks.GetShortLinkAsync(components, options).ContinueWith(task => {
if (task.IsCanceled)
{
Debug.LogError("GetShortLinkAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("GetShortLinkAsync encountered an error: " + task.Exception);
return;
}
// Short Link has been created.
Firebase.DynamicLinks.ShortDynamicLink link = task.Result;
Debug.LogFormat("Generated Short Dynamic Link: {0}", link.Url);
text.text = link.Url.ToString();
var warnings = new System.Collections.Generic.List<string>(link.Warnings);
if (warnings.Count > 0)
{
// Debug logging for warnings generating the short link.
}
});
}
result Logs
Long Dynamic Link: https://myapp.page.link/?afl=&amv=0&apn=com.myapp.myApp&ibi=com.myapp.myapp&ifl=&ipfl=&link=https://myapp.com
UnityEngine.Debug:Log(Object)
Generated Short Dynamic Link: https://myapp.page.link/?afl=&amv=0&apn=com.myapp.myApp&ibi=com.myapp.myapp&ifl=&ipfl=&link=https://myapp.com
UnityEngine.Debug:LogFormat(String, Object[])
EDIT:
How I did it:
You can't shorten dynamic links with Firebase SDK in the editor but I used REST API and UnityWebRequest for testing short links inside the editor and it worked
IEnumerator HTTPRequestShortLink(string longDynamicLink)
{
WWWForm form = new WWWForm();
form.AddField("longDynamicLink", longDynamicLink);
// trigger of function is HTTP request. this link is the trigger for that func
UnityWebRequest www = UnityWebRequest.Post("https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=[YOUR_API_KEY]", form);
//for getting response
www.downloadHandler = new DownloadHandlerBuffer();
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete! " + www.GetResponseHeader("response"));
string responseText = www.downloadHandler.text;
// Parse returned json
parsedJsonObject obj = parsedJsonObject.CreateFromJSON(responseText);
Debug.Log("shortLink: " + obj.shortLink);
Debug.Log("HTTP Response text: " + responseText);
}
}
I'm trying to send a POST request in Unity, but I can't do that as the function that is supposed to do that is skipped, I'm not sure why.
I have added logs to have an idea of what happens and "(post json) called" never appears even though the text saying that has gone past the function ((send game played): after postjson) appears in the logs.
private static void SendGamePlayed() {
int incrementedGamePlayed = GetGamePlayed () + 1;
EventObject anEvent = CreateEvent(EVENT_GAME_PLAYED, incrementedGamePlayed, null, null);
AbcEvent aAbcEvent = new AbcEvent (bundleID, appToken, anEvent);
string json = JsonUtility.ToJson (aAbcEvent);
// test
Debug.Log("(send game played): " + json);
PostJSON (json, EVENT_ROUTE);
Debug.Log ("(send game played): after postjson");
SetGamePlayed (incrementedGamePlayed);
}
private static IEnumerator PostJSON(string jsonString, string route) {
// test
Debug.Log("(post json) called");
string url = SERVER_URL + route;
var request = new UnityWebRequest(url, "POST");
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonString);
request.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
Debug.Log ("(post json) before sending");
yield return request.Send();
Debug.Log ("(post json) after sending");
if (request.error != null) {
Debug.Log("request error: " + request.error);
} else {
Debug.Log("request success: " + request.downloadHandler.text);
}
}
Thanks for your help!
You're using Unity's feature for asynchronous operations. To understand the concepts, read up on generators and Unity coroutines. To run your function, call the StartCoroutine function like this:
StartCoroutine(PostJSON (json, EVENT_ROUTE));
Edit:
More specifically: You're creating a coroutine (PostJson) that calls an asynchronous function (Send).
I am trying to build a generic function that will run a simple HTTP Get request using various possible URL Params.
I want to be able to receive a flexible number of strings as a parameter and add them one by one as a URL parameter in the request.
Here's my code so far, I am trying to build a List but for some reason I just can't muster a workign solution..
public static void GetRequest(List<string> lParams)
{
lParams.Add(header1);
string myURL = "";
HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create(string.Format(myURL));
WebReq.Method = "GET";
HttpWebResponse WebResp = (HttpWebResponse)WebReq.GetResponse();
Stream Answer = WebResp.GetResponseStream();
StreamReader _Answer = new StreamReader(Answer);
sContent = _Answer.ReadToEnd();
}
Thanks!
I think you need this:
private static string CreateUrl(string baseUrl, Dictionary<string, string> args)
{
var sb = new StringBuilder(baseUrl);
var f = true;
foreach (var arg in args)
{
sb.Append(f ? '?' : '&');
sb.Append(WebUtility.UrlEncode(arg.Key) + '=' + WebUtility.UrlEncode(arg.Value));
f = false;
}
return sb.ToString();
}
Not so complex version with comments:
private static string CreateUrl(string baseUrl, Dictionary<string, string> parameters)
{
var stringBuilder = new StringBuilder(baseUrl);
var firstTime = true;
// Going through all the parameters
foreach (var arg in parameters)
{
if (firstTime)
{
stringBuilder.Append('?'); // first parameter is appended with a ? - www.example.com/index.html?abc=3
firstTime = false; // All other following parameters should be added with a &
}
else
{
stringBuilder.Append('&'); // all other parameters are appended with a & - www.example.com/index.html?abc=3&abcd=4&abcde=8
}
var key = WebUtility.UrlEncode(arg.Key); // Converting characters which are not allowed in the url to escaped values
var value = WebUtility.UrlEncode(arg.Value); // Same goes for the value
stringBuilder.Append(key + '=' + value); // Writing the parameter in the format key=value
}
return stringBuilder.ToString(); // Returning the url with parameters
}