In the realm of game development, efficiency is key—both in terms of gameplay mechanics and behind-the-scenes processes. Recently, while playing "Clothing Store Simulator", a game published by KiKi Games, I encountered an issue that piqued my curiosity and drove me to investigate further. The issue involved excessive and unnecessary API calls to a time service, which I believed could impact the game's performance and the API provider's resources. This blog post details my findings and offers a proof-of-concept solution to optimize API usage in the game.
While navigating through various menus in "Clothing Store Simulator," I noticed repeated network activity directed at an external time service, WorldTimeAPI.org. This activity included frequent API calls whenever I accessed certain in-game menus, such as the Clothing Pass and leaderboards, as well as during seemingly unrelated actions like restocking shelves.
Upon closer inspection, I found that over the course of just 18 minutes and 22 seconds, the game made 92 GET requests to the API—equating to roughly one request every 12 seconds. Such frequent queries seemed excessive, particularly for routine in-game actions that shouldn't require constant time synchronization.
Excessive API calls can have several negative impacts:
WorldTimeAPI.org provides a valuable service, often for free or with limited resources. It's important to exercise care and respect for such services to avoid overburdening them.
Source: https://worldtimeapi.org/pages/faqs#commercial-apps
The FAQ section of WorldTimeAPI outlines several important considerations:
As developers, it’s crucial to use such services efficiently and ethically, respecting their limitations and contributing where possible to ensure their continued availability for the broader community.
To better understand the issue, I initially used ProcessExplorer to monitor the game's network connections, identifying patterns of frequent API calls. To gain deeper insights, I utilized Wireshark to filter and capture the specific network traffic. Through this analysis, I observed that API calls were being made not just during relevant moments (e.g., accessing the Clothing Pass) but also during routine actions like restocking shelves. This pattern indicated that the game was treating the Time API more like a continuous timekeeper rather than a reference point for periodic synchronization.
This analysis was conducted on version 0.3.65 of the game.
To address this, I developed a proof-of-concept solution designed to reduce the frequency of API calls while maintaining the necessary functionality. The key to this solution lies in treating the Time API like an NTP service—querying it once for an initial time sync and then maintaining an internal time incrementing mechanism.
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class TimeManager : MonoBehaviour
{
public static TimeManager Instance;
// The synced time from the API
private DateTime syncedTime;
// Time interval for updating the internal clock (in seconds)
private float updateInterval = 1f;
// Timer to keep track of the elapsed time since last update
private float elapsedTime = 0f;
// API endpoint for getting the current UTC time
private const string timeApiUrl = "http://worldtimeapi.org/api/timezone/Etc/UTC";
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
private void Start()
{
StartCoroutine(SyncTimeWithApi());
}
private void Update()
{
// Increment the time manually based on the elapsed time
elapsedTime += Time.deltaTime;
if (elapsedTime >= updateInterval)
{
syncedTime = syncedTime.AddSeconds(elapsedTime);
elapsedTime = 0f;
}
}
private IEnumerator SyncTimeWithApi()
{
UnityWebRequest request = UnityWebRequest.Get(timeApiUrl);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
TimeApiResponse response = JsonUtility.FromJson(request.downloadHandler.text);
syncedTime = DateTime.Parse(response.utc_datetime);
}
else
{
Debug.LogError("Failed to sync time with API: " + request.error);
syncedTime = DateTime.UtcNow; // Fallback to system time if API call fails
}
}
// Public method to get the current time
public DateTime GetCurrentTime()
{
return syncedTime;
}
}
// Class to represent the JSON response from the API
[Serializable]
public class TimeApiResponse
{
public string utc_datetime;
}
Here's how it works:
TimeManager
class, which then handles all time-related queries internally.This case study underscores the importance of mindful API usage in game development. By optimizing the way external services are utilized, we can enhance performance, reduce unnecessary load on external resources, and create a more resilient and efficient game.
If you're a developer working with similar API-dependent features, I encourage you to consider implementing a similar approach. The proof-of-concept code I developed for "Clothing Store Simulator" can easily be adapted to other games or applications that rely on periodic data from external sources.