How it works
You put a vote link in your game and on your site. A player opens it and votes for your server. Your server asks us whether that player voted and gives whatever reward you choose. One vote per player every 12 hours, so there is a reason to come back tomorrow.
Votes decide your position on the top list, so the players you send here lift your own ranking.
1. Get your link and key
List your server (it is free), then open Votes & API in your account. Each listed server has two things there:
- A vote link like
https://mmonova.com/vote.php?id=123. This is public. Give it to your players. - An API key, 48 characters. This is a secret. It belongs on your server only, never in the client, never on a web page. Anyone holding it can read your stats and mark votes as rewarded.
You can roll the key at any time if it leaks. The old one stops working immediately.
2. Put the link where players see it
The usual places: a .vote voiced command, a community board page, your Discord, and a button on your website. Anything that opens the link in a browser works.
3. Ask us whether they voted
Two calls, both plain GET requests.
status only reads. Call it as often as you like.
https://mmonova.com/api.php?action=status&key=YOUR_KEY&ip=PLAYER_IP{"success":true,"voted":true,"claimed":false,"cooldown_seconds":39600}claim hands out the vote. It returns true the first time and false every time after, for that vote. That is what stops a player collecting the same reward twice, even if your code runs again after a crash or a relog.
https://mmonova.com/api.php?action=claim&key=YOUR_KEY&ip=PLAYER_IP{"success":true,"reward":true}If your vote module expects a bare 1 or 0 instead of JSON, which most of the older ones do, add &format=plain and point it at this URL. Often that is the whole integration.
https://mmonova.com/api.php?action=claim&key=YOUR_KEY&ip=PLAYER_IP&format=plainJava, for an L2J voiced command
Reads the plain format, so there is no JSON library to add.
private static final String VOTE_API = "https://mmonova.com/api.php";
private static final String VOTE_KEY = "your-48-character-key";
/** True when the player has a vote waiting to be rewarded. Marks it rewarded. */
private static boolean claimVote(String playerIp) throws IOException
{
String url = VOTE_API + "?action=claim&format=plain"
+ "&key=" + VOTE_KEY
+ "&ip=" + URLEncoder.encode(playerIp, StandardCharsets.UTF_8);
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)))
{
return "1".equals(reader.readLine().trim());
}
finally
{
connection.disconnect();
}
}Call it off the player's thread. A network call inside a voiced command handler blocks whichever thread runs it, and our answer takes as long as the internet takes.
public boolean useVoicedCommand(String command, Player player, String params)
{
final String ip = player.getIPAddress();
ThreadPool.execute(() ->
{
boolean rewarded;
try
{
rewarded = claimVote(ip);
}
catch (IOException e)
{
player.sendMessage("Could not reach the vote service. Try again in a minute.");
return;
}
if (!rewarded)
{
player.sendMessage("Vote first at https://mmonova.com/vote.php?id=123, then type .vote again.");
return;
}
player.addItem("Vote reward", 57, 1000000, player, true);
player.sendMessage("Thank you for voting.");
});
return true;
}PHP, for a website reward page
$key = 'your-48-character-key';
$ip = $_SERVER['REMOTE_ADDR'];
$url = 'https://mmonova.com/api.php?action=claim&key=' . $key . '&ip=' . urlencode($ip);
$response = json_decode(@file_get_contents($url), true);
if (!empty($response['reward']))
{
// Give the reward here. This branch runs once per vote.
}
else
{
echo 'Vote first at https://mmonova.com/vote.php?id=123';
}Things that catch people out
- Use the player's real IP, the one their game client connects from. If your login server sits behind a proxy, pass the forwarded address, not the proxy's.
- Call claim once per reward, not once per check. Use
statusfor anything you display, andclaimat the moment you actually hand the items over. - Give the reward after claim returns true, not before. If your item grant fails you can log it, but a claimed vote does not come back.
- One player, one vote per 12 hours, per server.
cooldown_secondstells you how long is left, so you can show a timer. - Players on the same connection share an address. A household or an internet cafe counts as one vote. Every toplist works this way.
- Do not ask for a vote before letting someone play. Reward it, do not gate on it.
Limits and errors
5000 calls per hour per key, which is far more than a busy server needs. Past that you get HTTP 429.
Errors come back as {"success":false,"error":"..."} with a matching status: 401 if the key is missing, 403 if it is unknown or the server is still awaiting review, 400 if the IP is missing or malformed. In format=plain every failure is simply 0, so treat 0 as "no reward" and move on.
Your own totals and ranking:
https://mmonova.com/api.php?action=stats&key=YOUR_KEY{"success":true,"server":"Your Server","votes":{"day":41,"month":880,"total":880},"rank":3}Stuck?
Email [email protected] with your server name and what you tried. See also the FAQ and the Terms.