Real time in-game notification for turn based games? RLY?!

YES TODAY!!! IN YOUR BROWSER !!!

I have developed a feature for turn based games, which lights a button orange when you or another user presses it. If everyone is waiting for just one player, the button changes color to green to make them feel the pressure and hurry. After they are done, they just turn the light off, which gives you signal to reload your UI and start the next round.

Clipboard06

  • refresh rate is 5 seconds
  • supported signals: OFF, READY, HURRY

OFF
image

  • press the button to light it orange and say you are READY

READY
READY

  • at this point you can press the button again to revoke your readiness

HURRY
HURRY

  • all players but one are READY, this tells the last one to HURRY
  • press again to turn it OFF and signal players to reload their UI

You can try it with a default application key, just follow steps 6 and 7.
If you like it, generate a unique key for your game (step 1).


  1. Go to https://paste.ee/login and log in with
    login:nptriton
    password:nptriton

  2. Go to https://paste.ee/account/api/developer or API and create new application
    I suggest to use your game’s id as Application Name.

  3. Next click the ACTIONS button on right for your game and on the AUTHORIZATION PAGE

  4. There click SUBMIT without any changes (everything enabled)

  5. Then copy the key
    image
    You can use this one key for everyone in the game or you can generate many unique keys.

  6. Copy this script and replace the key with your own key.

javascript:by:Qwerty:key='uodthyRYQC1YjoGEe5o3DR9YmLti6XIZfhNldsvXZ';eval(atob('Ly8gUGFzdGVlIG1ldGhvZHMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gaHR0cHM6Ly9wYXN0ZWUuZ2l0aHViLmlvL2RvY3MvI3N1Ym1pdC1hLW5ldy1wYXN0ZQoKYXN5bmMgZnVuY3Rpb24gd3JpdGUodWlkID0gIiIpIHsKICByZXR1cm4gZmV0Y2goYGh0dHBzOi8vYXBpLnBhc3RlLmVlL3YxL3Bhc3Rlcz9rZXk9JHtrZXl9YCwgewogICAgbWV0aG9kOiAnUE9TVCcsCiAgICBoZWFkZXJzOiB7CiAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicKICAgIH0sCiAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7CiAgICAgICJkZXNjcmlwdGlvbiI6IHVpZCwKICAgICAgInNlY3Rpb25zIjogW3sKICAgICAgICAiY29udGVudHMiOiAnMScKICAgICAgfV0KICAgIH0pCiAgfSkKfQoKYXN5bmMgZnVuY3Rpb24gbGlzdCgpIHsKICByZXR1cm4gZmV0Y2goYGh0dHBzOi8vYXBpLnBhc3RlLmVlL3YxL3Bhc3Rlcz9rZXk9JHtrZXl9YCkudGhlbihyID0+IHIuanNvbigpKQp9Cgphc3luYyBmdW5jdGlvbiByZW1vdmUoaWQpIHsKICByZXR1cm4gZmV0Y2goYGh0dHBzOi8vYXBpLnBhc3RlLmVlL3YxL3Bhc3Rlcy8ke2lkfT9rZXk9JHtrZXl9YCwgewogICAgbWV0aG9kOiAnREVMRVRFJywKICB9KQp9Cgphc3luYyBmdW5jdGlvbiByZW1vdmVBbGwoKSB7CiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgbGlzdCgpCiAgcmV0dXJuIFByb21pc2UuYWxsKHJlc3VsdC5kYXRhLm1hcChpdGVtID0+IHJlbW92ZShpdGVtLmlkKSkpCn0KCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpjb25zdCBnZXRVc2VySWQgPSAoKSA9PiB3aW5kb3cuTmVwdHVuZXNQcmlkZS5hY2NvdW50LnVzZXJfaWQKY29uc3QgZ2V0UGxheWVyQ291bnQgPSAoKSA9PiB3aW5kb3cuTmVwdHVuZXNQcmlkZS51bml2ZXJzZS5wbGF5ZXJDb3VudAoKYXN5bmMgZnVuY3Rpb24gdXBkYXRlVUkoKSB7CiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgbGlzdCgpCgogIGNvbnN0IGNvdW50ID0gcmVzdWx0LmRhdGEubGVuZ3RoCiAgY29uc3QgcGxheWVyQ291bnQgPSBnZXRQbGF5ZXJDb3VudCgpCiAgY29uc3QgY3VycmVudFBhc3RlZUlkID0gcmVzdWx0LmRhdGEuZmluZChwYXN0ZWUgPT4gcGFzdGVlLmRlc2NyaXB0aW9uID09PSBnZXRVc2VySWQoKSk/LmlkCgogIGlmIChjb3VudCA9PT0gcGxheWVyQ291bnQgLSAxKSByZXR1cm4gdHVybkZ1bGwoKQogIGlmIChjb3VudCA+IDApIHJldHVybiB0dXJuRGltbWVkKGN1cnJlbnRQYXN0ZWVJZCkKICByZXR1cm4gdHVybk9mZigpCn0KCmZ1bmN0aW9uIHBvbGxQYXN0ZWUoKSB7CiAgaWYgKHdpbmRvdy5RUV9yZWFkeXN0YXRlX2ludGVydmFsID09PSB1bmRlZmluZWQpIHsKICAgIHdpbmRvdy5RUV9yZWFkeXN0YXRlX2ludGVydmFsID0gc2V0SW50ZXJ2YWwodXBkYXRlVUksIDUwMDApCiAgICB1cGRhdGVVSSgpCiAgfQp9CgpmdW5jdGlvbiBzZXRCdXR0b24oY2xhc3NOYW1lID0gJycsIG9uY2xpY2sgPSAoKSA9PiB7IH0pIHsKICB3aW5kb3cuUVFfcmVhZHlzdGF0ZV9lbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ2J1dHRvbl91cCcsICdidXR0b25fZG93bicsICdidXR0b25faG92ZXInKQogIHdpbmRvdy5RUV9yZWFkeXN0YXRlX2VsZW1lbnQuY2xhc3NMaXN0LmFkZChjbGFzc05hbWUpCiAgd2luZG93LlFRX3JlYWR5c3RhdGVfZWxlbWVudC5vbmNsaWNrID0gb25jbGljawp9CgpmdW5jdGlvbiB0dXJuRGltbWVkKGlkKSB7IHNldEJ1dHRvbignYnV0dG9uX2hvdmVyJywgaWQgPyAoKSA9PiBkZW5vdGlmeShpZCkgOiBub3RpZnkpIH0KZnVuY3Rpb24gdHVybkZ1bGwoKSB7IHNldEJ1dHRvbignYnV0dG9uX2Rvd24nLCBkZW5vdGlmeUFsbCkgfQpmdW5jdGlvbiB0dXJuT2ZmKCkgeyBzZXRCdXR0b24oJ2J1dHRvbl91cCcsIG5vdGlmeSkgfQoKYXN5bmMgZnVuY3Rpb24gbm90aWZ5KCkgewogIGF3YWl0IHdyaXRlKGdldFVzZXJJZCgpKQogIHVwZGF0ZVVJKCkKfQoKYXN5bmMgZnVuY3Rpb24gZGVub3RpZnkoaWQpIHsKICBhd2FpdCByZW1vdmUoaWQpCiAgdXBkYXRlVUkoKQp9Cgphc3luYyBmdW5jdGlvbiBkZW5vdGlmeUFsbCgpIHsKICBhd2FpdCByZW1vdmVBbGwoKQogIHVwZGF0ZVVJKCkKfQoKZnVuY3Rpb24gaW5pdCgpIHsKICBpZiAod2luZG93LlFRX3JlYWR5c3RhdGVfZGlkSW5pdCA9PT0gdHJ1ZSkgcmV0dXJuCgogIHdpbmRvdy5RUV9yZWFkeXN0YXRlX2RpZEluaXQgPSB0cnVlCiAgZG9jdW1lbnQKICAgIC5xdWVyeVNlbGVjdG9yKCcjY29udGVudEFyZWEgPiBkaXYgPiBkaXYud2lkZ2V0LmZ1bGxzY3JlZW4gPiBkaXY6bnRoLWNoaWxkKDMpID4gZGl2Om50aC1jaGlsZCg1KScpCiAgICAuaW5zZXJ0QWRqYWNlbnRIVE1MKAogICAgICAnYmVmb3JlYmVnaW4nLAogICAgICAnPGRpdiBjbGFzcz0id2lkZ2V0IGJ1dHRvbiBidXR0b25fdXAiIHRhYmluZGV4PSIwIiBzdHlsZT0ibGVmdDogNDAwcHg7dG9wOiA4cHg7d2lkdGg6IDMycHg7aGVpZ2h0OiAzMnB4OyIgaWQ9InJlYWR5c3RhdGUiPjxkaXYgY2xhc3M9IndpZGdldCBpY29uLWZsYXNoIHR4dF9jZW50ZXIgaWNvbl9idXR0b24iIHN0eWxlPSJsZWZ0OiAwcHg7IHRvcDogMTZweDsgd2lkdGg6IDMycHg7Ij48L2Rpdj48ZGl2IGNsYXNzPSJ3aWRnZXQgdHh0X2NlbnRlciBjb2xfd2FybmluZyByYWQ0IiBzdHlsZT0ibGVmdDogOHB4OyB0b3A6IDhweDsgd2lkdGg6IDI0cHg7IGhlaWdodDogMjRweDsgZGlzcGxheTogbm9uZTsiPjwvZGl2PjwvZGl2PicKICAgICkKICB3aW5kb3cuUVFfcmVhZHlzdGF0ZV9lbGVtZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiI3JlYWR5c3RhdGUiKQogIHBvbGxQYXN0ZWUoKQp9Cgppbml0KCk='))
Click to reveal the full code
var key = 'uodthyRYQC1YjoGEe5o3DR9YmLti6XIZfhNldsvXZ'


// Pastee methods --------------------------------------------------------------
// https://pastee.github.io/docs/#submit-a-new-paste

async function write(uid = "") {
  return fetch(`https://api.paste.ee/v1/pastes?key=${key}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      "description": uid,
      "sections": [{
        "contents": '1'
      }]
    })
  })
}

async function list() {
  return fetch(`https://api.paste.ee/v1/pastes?key=${key}`).then(r => r.json())
}

async function remove(id) {
  return fetch(`https://api.paste.ee/v1/pastes/${id}?key=${key}`, {
    method: 'DELETE',
  })
}

async function removeAll() {
  const result = await list()
  return Promise.all(result.data.map(item => remove(item.id)))
}

// -----------------------------------------------------------

const getUserId = () => window.NeptunesPride.account.user_id
const getPlayerCount = () => window.NeptunesPride.universe.playerCount

async function updateUI() {
  const result = await list()

  const count = result.data.length
  const playerCount = getPlayerCount()
  const currentPasteeId = result.data.find(pastee => pastee.description === getUserId())?.id

  if (count === playerCount - 1) return turnFull()
  if (count > 0) return turnDimmed(currentPasteeId)
  return turnOff()
}

function pollPastee() {
  if (window.QQ_readystate_interval === undefined) {
    window.QQ_readystate_interval = setInterval(updateUI, 5000)
    updateUI()
  }
}

function setButton(className = '', onclick = () => { }) {
  window.QQ_readystate_element.classList.remove('button_up', 'button_down', 'button_hover')
  window.QQ_readystate_element.classList.add(className)
  window.QQ_readystate_element.onclick = onclick
}

function turnDimmed(id) { setButton('button_hover', id ? () => denotify(id) : notify) }
function turnFull() { setButton('button_down', denotifyAll) }
function turnOff() { setButton('button_up', notify) }

async function notify() {
  await write(getUserId())
  updateUI()
}

async function denotify(id) {
  await remove(id)
  updateUI()
}

async function denotifyAll() {
  await removeAll()
  updateUI()
}

function init() {
  if (window.QQ_readystate_didInit === true) return

  window.QQ_readystate_didInit = true
  document
    .querySelector('#contentArea > div > div.widget.fullscreen > div:nth-child(3) > div:nth-child(5)')
    .insertAdjacentHTML(
      'beforebegin',
      '<div class="widget button button_up" tabindex="0" style="left: 400px;top: 8px;width: 32px;height: 32px;" id="readystate"><div class="widget icon-flash txt_center icon_button" style="left: 0px; top: 16px; width: 32px;"></div><div class="widget txt_center col_warning rad4" style="left: 8px; top: 8px; width: 24px; height: 24px; display: none;"></div></div>'
    )
  window.QQ_readystate_element = document.querySelector("#readystate")
  pollPastee()
}

init()

  1. Create a bookmarklet
    How to create a Bookmarklet?

The script checks every five seconds for an activity.

PS:
Sadly, this solution doesn’t work any more?

1 Like