У меня есть веб-сервер, который содержит базу данных SQL. На этом сервере / базе данных есть два «пользователя». Одним из них является фактический пользователь, которому они будут отправлять изменения через UnityWebRequest, используя HTTP: Post. Это внесет изменения в базу данных.
У меня есть другая система, которая также находится в Unity, о которой нужно уведомлять или каким-либо образом отслеживать каждый раз, когда вносятся изменения в конкретную таблицу в базе данных. Я не знаю, как следить за изменениями в таблице, не делая выборочных обращений к базе данных.
Что я пробовал
У меня есть функция единства, которая вызывает веб-сервер через HTTP: Post. Веб-сервер заходит в бесконечный цикл, делая вызовы к базе данных что-то вроде
$variable = $_REQUEST['variable_to_monitor'];
$stmt = $pdo->prepare("SELECT variable_to_monitor FROM table_name;")
while(true){
$stmt->execute();
results = $stmt->fetchAll()[0];
if ($variable != results['variable_to_monitor']){
die(results['variable_to_monitor']);
}
}
Это поддерживает веб-сервер и делает слишком много обращений к базе данных.
Я хотел бы, чтобы Unity могла сделать один вызов веб-серверу, который задает ему определенное состояние, веб-сервер будет сравнивать указанное состояние с базой данных до тех пор, пока база данных не изменится, а затем, как только изменения в БД откликнутся на единицу с обновленным состоянием. Я хочу быть в состоянии сделать это, не делая миллион вызовов SELECT для базы данных в секунду.
Код единства
void Update()
{
if(hasResponse)
{
hasResponse = false;
StartCoroutine(SendRequest());
}
}
IEnumerator SendRequest(WWWForm form = null, Action<string> callback = null)
{
if(null == form) form = new WWWForm();
form.AddField("request", "monitor_variable");
form.AddField("variable_to_monitor", this.variable_to_monitor);
UnityWebRequest www = UnityWebRequest.Post(url, form);
yield return www.SendWebRequest();
if (www.isNetworkError)
{
Debug.Log("Network Error");
}
else if (www.isHttpError)
{
Debug.Log("Http Error");
}
else
{
if(null == callback)
{
Debug.Log(www.downloadHandler.text);
}
else
{
if (!www.downloadHandler.text.Contains("New Request Started"))
{
hasResponse = true;
callback(www.downloadHandler.text);
}
else
{
Debug.Log(www.downloadHandler.text);
}
}
}
}
Конечно, вы можете и всегда должны ждать в Unity UnityWebRequest заканчивать! Вы должны всегда использовать, например, сопрограммная
И чем вы можете, например, добавьте временной сдвиг, чтобы вы не делали новый запрос прямо сейчас, но, возможно, подождите несколько секунд.
Я лично также добавил бы обратные вызовы для ответа, который делает его более гибким и многократно используемым, например.
public void MakeRepeatedRequests(string url, float timeOffset, Action<string> successCallback, Action<string> errorCallback)
{
StartCoroutine(RepeatedRequestRoutine(url, timeOffset, successCallback, errorCallback);
}
private IEnumerator RepeatedRequestRoutine(string url, float timeOffset, Action<string> successCallback, Action<string> errorCallback)
{
// No worries this still looks like a while loop (well it is ^^)
// but the yield makes sure it doesn't block Unity's UI thread
while(true)
{
// Configure your request
var request = UnityWebRequest.Post(url);
// Make the request and wait until the it has finished (has a response or timeout)
// a yield kind of tells this routine to leave, let Unity render this frame
// and continue from here in the next frame
yield return request.SendWebRequest();
if(request.isNetworkError || request.isHttpError)
{
errorCallback?.Invoke(request.error);
}
else
{
successCallback?.Invoke(request.downloadHandler.text);
}
// Now wait for the timeOffset
yield return new WaitForSeconds(timeOffset);
}
}
Теперь вы можете назвать это, например, за ожидание через 2 секунды после последнего запроса законченный
// ...
MakeRepeatedRequests ("http://example.com", 2.0f, OnSuccess, OnError);
// ...
private void OnSuccess(string success)
{
Debug.LogFormat(this, "Nice it worked! Server said:/"{0}/"", success);
// Do something e.g. using success
}
private void OnError(string error)
{
Debug.LogFormat(this, "Oh no, request failed with \"{0}\"", error);
}
или также сделать то же самое, используя лямбда-выражения
MakeRepeatedRequests ("http://example.com", 2.0f,
success =>
{
Debug.LogFormat(this, "Nice it worked! Server said:/"{0}/"", success);
// Do something e.g. using success
},
error =>
{
Debug.LogFormat(this, "Oh no, request failed with \"{0}\"", error);
// Do something else
});
Даже если вы не хотите timeOffset
По крайней мере, это ожидает завершения каждого запроса, прежде чем начинать следующий, поэтому вашему серверу нужно только выполнить один запрос к БД и вернуть либо успех, либо ошибку.
В вашем случае я бы в любом случае ответил с успехом (200) (потому что ошибка должна отправляться только в том случае, если на сервере действительно была ошибка), но либо отправляю пустой string
или какой-то предопределенный, который вы можете проверить в Unity (например, например, "!NO_CHANGE!"
).
так на стороне сервера без цикла что-то вроде, например
results = "SELECT * FROM table_name;"if ($_REQUEST['variable_name'] != results['variable_name']){
echo results['variable_name'];
} else {
echo "!NO_CHANGE!";
}
чем вы могли бы позже проверить это в Unity
if(string.Equals(success, "!NO_CHANGE!")) return;
Других решений пока нет …