【iot】こっそり販売してるEcho Input@スマートスピーカ

image

話しかけると色々答えてくれるスマートスピーカーですが、人気のAmazon Echoシリーズ

ありそーで無かったのがこれ、Echo Inputという製品。2980円とめっちゃ安い。

∠( ゚д゚)/セール時はもっと安くなるんか?

マイクと本体だけで、スピーカー部分は後付けなんですよ、なので、好きな(ステレオ)スピーカーやアンプに接続できる(アナログかBluetoothで)

色は、黒しか無いようです。

既に、去年にアメリカでは発売されてたんですが、日本では特にプロモーションも無く、こっそり販売されてた。

(;´∀`)

image

現行、Amazon Echoでは、唯一USB電源だけで稼働するのも注目なんですよ。

屋外や車内でも利用しやすい訳です(ネットはテザリングで)

image

Echoの利点のひとつとも言える、音楽サービスが380円で利用できるのも大きい(Amazon music Unlimited)

知人から聞いた話では、お婆ちゃんの話相手にも、なり得るんだそーですよ・・・・。

(;゚д゚)・・・

これ、アリじゃないかと。

【GAME】ARMA3 EXILE MODサーバの旅その90

image

追記:サーバOSアップデート更新@4/19 AM11から稼働

追記:ガソスタでの自動給油無し?!

追記:サーバに強い味方参上!(物理)

追記:ゾンビ感染するらしいので注意@世界保険機関WHO

追記:凄すぎるArma3のムービー動画

追記:北国のまだ咲いてない桜

追記:ヘリパトロールを改良

追記:昨日のテストプレー

追記:今後の追加要素予定まとめ

追記:サーバ微調整しましたx2

さらにパワーアップしました。

鯖味噌サーバの密告書(SabamisoLeaks)

拠点と車両のデータを追加しました。

車両が盗難やイタズラにあったり、拠点が抜かれたり、家賃支払忘れなど・・・どこからでもスマホで確認できます。

(;^ω^)

ページがでかくなってしまったので、画面下部にリンクを設けてます。

追記:サーバ微調整しました

全域に渡り、僅かぁ~に微調整しました。難易度変わらず(若干、バンビ寄りに)PM7から適用。

良いの見っけた。

( ゚д゚)!

ハンドガンサプ増殖バグの対策、町AIの若干の調整、車両パトロールの調整(プレーヤー要望、不平不満はgさんへ)、アイテム価格の誤り修正

総じて、難易度アップとなります。

4/17 PM19頃のリスタートで適用です。

m9( ゚д゚)!

追記:今後の追加要素予定まとめ

image

昨夜のDiscordのボードにて、色々と情報を頂きました。

やるか、やらないか?はさておき、まとめておきます。

オーバーデス対策(要調査、DB重複登録バグ)

パトロール車両搭乗者を増やせよ。コラ(プレーヤーさん要望)済み

バスに20人位AI乗せたらどうよ

ゾンビ感染と治療

ガソリンスタンドでの給油を無効にする(原油をクラフト?)(ヘリ運用が地獄になる)

以下は、勝手に導入しようとしてるリスタートタイミングの新要素(鯖味噌オリジナル)

古くなった車両は壊れやすくなる(どこかが20%程度壊れる)

ガソリンが自然に減っていく(気化する)

生もの食品が腐って消える

アヒル人形が何かに変身する

アヒル人形が現れる(服・車両)※条件:前回稼働時にイン

ポップタブが少し無くなる※ポッケに穴が開いてる

追記:昨日のテストプレー

20190415214300_1

何かで使えるかもと、ナイスなシーンを撮り溜めていってます。砂嵐の中、薄っすら見える人影がいい感じ。

20190417211516_1

箱、あんな所にあるよ・・w

20190417212002_1

パトロールヘリをスコープでズームしてみた。

20190417212158_1

音がした方に行ったら、プレーヤーさんの車両を見つけた。

ガチ勢やん。

(;^ω^)

後を追い掛けて、IEDでも仕掛けたくなる。

追記:ヘリパトロールを改良

image

ヘリパトロールは、ランダムでどこかの町が選ばれて、適当に飛んでるだけなのですが。。

イマイチ、”脅威”じゃないんですよね。

これを大きく改造しました!

(;^ω^)

マップを所狭しと、滑空しながら要所をパトロールするようにしました。

まだ、動作未確認ですけども。

追記:北国のまだ咲いてない桜

P_20190418_132106

どうでしょ、今週末くらいかな。。

追記:凄すぎるArma3のムービー動画

クオリティ高すぎ。

追記:ゾンビ感染するらしいので注意@世界保険機関WHO

ゾンビに攻撃されると、まれに感染してしまう場合があるようです。まだ治療方法が無いので、注意です。

:(;゙゚’ω゚’):

追記:サーバに強い味方参上!(物理)

P_20190418_164337

最近暑くなってきたからね・・ケースに風当ててますが。

(;´Д`)

追記:ガソスタでの自動給油無し?!

20190418181727_1

以前からやりたかった事のひとつ・・、ガソリンスタンドで自動給油できなく、したい。

だが、なかなか仕組みが分からなくて、諦めてたんですが。

先程、テストで成功した。

(;´∀`)

死ぬほど簡単やった。発想の転換が必要だったようです。

{
_x enableSimulation false;
}foreach nearestTerrainObjects [this, [“Fuelstation”], 100];

テストしたコードがこれ。

MAPのオブジェクトのシミュレートを無効にするだけ、という。実際に組み込むには、MAP全域でサーチを掛けなきゃいけませんでどもね(あと、enableSimulationGlobalの方で無効にせにゃあかん)

追記:サーバOSアップデート更新@AM11から稼働

image

OS更新が溜まりまくっていたので、まとめて更新掛けてます。

サーバをネットに公開すると、何気に、外から結構な数の攻撃をされてます・・。

(;^ω^)しょーない

開けてるポートは、Arma3のみなので、心配無用ですが、万一に備えて、OSの更新も忘れず実施します。

P_20190419_110530

みんな、すまぬ・・。プリンを食させていただく。

【Arma3】スコアをウェブ公開する方法@Exileサーバ

image

動作サンプルはこちら(鯖味噌サーバ密告書)

ゲーム動作時でもリアルタイムのプレーヤースコア公開が可能です。

Exile ModによるArma3  Dedicated Serverでサーバ運用されてる管理者さんの参考にさればと、公開してます。

なお、こちらで公開しているのは、Arma3側もウェブサーバ側もLinuxですので、Windowsの場合は、適宜書き換えてください。

また、動けばOKッ!というスタイルで短時間で書いた代物ですので、小汚く低品質コードなのは了承ください。

(;^ω^)参考程度に・・

分からないという方は、ご連絡ください。

動作仕組み

Arma3サーバ側

Cronにて、以下を定期的に実行するようにします。当方では1時間毎。

MySQLデータベースから必要なデータを取り込み、JSONファイルを複数生成します。

SCP(秘密鍵)にて、Webサーバへコピーします。念の為、ユーザーのホームへコピーします。

ウェブサーバ側

コピーされたJSONファイルへ、ウェブ公開ホルダーにリンクを貼っておきます。

ここから先は、HTMLのお話なので、割愛しますが、当方では、eisenbraun-columnsで、そのままJSONデータを表示しています。とっても楽です。

SQLコード

まずは、欲しいデータの要求が正常に行えるようSQLコードを作ります。

image

使い慣れたDBユーティリティで、SQL文を用意します。Exileのデータベースはとてもシンプルなので、見るだけで分かるはずです。

注意するのは、余計なデータまで入れ込んでしまうと、(知ってる人が)JSONファイルに直接アクセスして、表示外のデータまで見られてしまいます。

データ転送量も鑑みて、ミニマムにするべきです。

リスペクト上位5名をランダム順(鯖缶除く)

SELECT * FROM
(SELECT a.name/*,a.score*/ FROM exile.account as a WHERE a.name <> “nabe” ORDER BY a.score DESC LIMIT 5) as rank
ORDER BY RAND();

キル数上位5名をランダム順(鯖缶除く)

SELECT * FROM
(SELECT a.name/*,a.kills*/ FROM exile.account as a WHERE a.name <> “nabe” ORDER BY a.kills DESC LIMIT 5) as rank
ORDER BY RAND();

最近50件のデスリスト(鯖缶除く)

SELECT
h.name,
h.died_at,
cast(round(h.position_x,0) as unsigned) as x,
cast(round(h.position_y) as unsigned) as y
FROM exile.player_history as h
WHERE name <> ‘nabe’
ORDER BY died_at DESC
LIMIT 50;

全プレーヤーの詳細リスト(名前無し)

SELECT
/*a.uid,a.name,*/
cast(round(p.damage*100,0) as unsigned) as damage,
cast(truncate(p.hunger,0) as unsigned) as hunger,
cast(truncate(p.thirst,0) as unsigned) as thirst,
p.money,
cast(round(p.position_x,0) as unsigned) as x,
cast(round(p.position_y,0) as unsigned) as y,
p.primary_weapon as w,
p.spawned_at as sp,
datediff(now(),p.spawned_at) as alive,
a.deaths,a.kills,a.score,
a.total_connections as tc,
last_disconnect_at as ld,
datediff(now(),a.last_disconnect_at) as last,
a.first_connect_at as fc,
datediff(now(),a.first_connect_at) as days
FROM exile.account as a LEFT JOIN exile.player as p ON a.uid = p.account_uid
WHERE a.name <> ‘DMS_PersistentVehicle’
ORDER BY a.last_disconnect_at DESC;

所有ポップタブのランキング上位10名

SELECT rank.name,CAST(rank.poptabs as UNSIGNED) as money FROM
     (SELECT a.uid,a.name,
     (IFNULL((SELECT SUM(p.money) FROM exile.player as p WHERE p.account_uid = a.uid),0)+
     IFNULL((SELECT SUM(v.money) FROM exile.vehicle as v WHERE v.account_uid = a.uid),0)+
     IFNULL((SELECT SUM(c.money) FROM exile.container as c WHERE c.account_uid = a.uid),0)) as poptabs
     FROM exile.account as a WHERE NOT a.name in (‘nabe’,’DMS_PersistentVehicle’) ORDER BY poptabs DESC LIMIT 10) as rank
ORDER BY money DESC;

所有エロ本のランキング上位10名

SELECT /*a.uid,*/a.name,
     CAST(
         IFNULL((SELECT
         TRUNCATE((LENGTH(CONCAT(p.backpack_magazines,p.uniform_magazines,p.vest_magazines))-LENGTH(REPLACE(CONCAT(p.backpack_magazines,p.uniform_magazines,p.vest_magazines),’Exile_Item_Magazine0′,”)))/LENGTH(‘Exile_Item_Magazine0’),0) as cnt1
         FROM exile.player as p
         WHERE p.account_uid = a.uid),0)+
         IFNULL((SELECT SUM(TRUNCATE((LENGTH(v.cargo_magazines)-LENGTH(REPLACE(v.cargo_magazines,’Exile_Item_Magazine0′,”)))/LENGTH(‘Exile_Item_Magazine0’),0)) as cnt2
         FROM exile.vehicle as v
         WHERE v.account_uid = a.uid),0)+
         IFNULL((SELECT SUM(TRUNCATE((LENGTH(c.cargo_magazines)-LENGTH(REPLACE(c.cargo_magazines,’Exile_Item_Magazine0′,”)))/LENGTH(‘Exile_Item_Magazine0’),0)) as cnt3
         FROM exile.container as c
         WHERE c.account_uid = a.uid),0)
     AS SIGNED) as books
FROM exile.account as a WHERE NOT a.name in (‘DMS_PersistentVehicle’) ORDER BY books DESC LIMIT 10;

現在の接続数と更新時間が欲しいので・・

SELECT CAST(COUNT(a.uid) as UNSIGNED) as connected,NOW() as tm FROM exile.account as a WHERE a.last_connect_at > a.last_disconnect_at;

当方で利用してるSQLは、こんな感じです。

お好きな、欲しいデータを出力するSQLを書いてそれぞれに用意します。

JSONを解釈するJavaScript側で、Decimal型をうまく扱えないようで、結構面倒です。なので、実数だけならSQL内でUNSIGNED型に変換しています。

Arma3サーバ側コード

CRONでの設定(定期的実行)

30 * * * * /home/****/arma3/getExileDB.sh

Windowsだと、タスクスケジューラとかかな?

ファイルを送信するシェルスクリプト

Arma3サーバが起動していれば、実行したいSQLをファイルにして、JSONファイルを生成して、まとめてウェブサーバへコピーするだけです。

Windowsだと、PowerShellやBATファイルでの記述になります。中身は結構変わりますが、やる事はシンプルなので。SCPの部分は、別途Termソフトで実現できるかと思います。

下記、青文字部分がCron内で動いてくれない様子なので、緑色文字のようにして、動作確認しました。こっちの方がシンプルw

■getExileDB.sh

#!/bin/sh

count=ps -e|grep -v grep arma3server|wc -l
if [ $count -ge 1 ]; then

count=pgrep arma3
if [ -n $count ]; then

     cd /home/****/arma3

    cat list_death.sql|python3 get_ExileDB.py > d.json
     cat list_players.sql|python3 get_ExileDB.py > p.json
     cat rank10_death.sql|python3 get_ExileDB.py > rd.json
     cat rank10_kill.sql|python3 get_ExileDB.py > rk.json
     cat rank10_score.sql|python3 get_ExileDB.py > rs.json
     cat rank10_money.sql|python3 get_ExileDB.py > rm.json
     cat rank10_books.sql|python3 get_ExileDB.py > rb.json
     cat connected.sql|python3 get_ExileDB.py > cn.json

    scp -i ~/.ssh/id_rsa /home/****/arma3/*.json (ユーザーID)@(Webサーバ):/(ウェブサーバ側コピー先
fi

SQL結果からJSONファイルを生成するPythonコード

Python3用なので、古いシステムなら注意。必要なら、pipでmysql.connectorをインストールします。

丸っとDBのパスワードが書かれるので、パーミッション設定は確実に(または他の方法で隠してください)

Windowsも、Pythonを別途インストールすればそのまま利用できます(別にPython経由しなくても、MySQLユーティリティ単体で出力できる・・かもしれない、未調査)

■get_ExileDB.py

import sys
import mysql.connector
import json
from datetime import date, datetime
import codecs

input_sql = sys.stdin.readlines()
# db
data = “”
conn = mysql.connector.connect(host=’DBサーバアドレス‘,port=’3306′,user=’DBユーザー‘,password=’DBパスワード‘,database=’exile’)
cursor = conn.cursor(dictionary=True)
try:
     cursor.execute(“”.join(input_sql))
     data = cursor.fetchall()
finally:
     cursor.close()
     conn.close()
def json_serial(obj):
     if isinstance(obj, (datetime, date)):
         return obj.isoformat()
     raise TypeError (“Type %s not serializable” % type(obj))
# convert
#print(data)
print(json.dumps(data, default=json_serial))

ウェブサーバ側コード

image

ウェブ公開ホルダーではなく、ユーザーホームのsabamisoにコピーするようにしてますので、事前に、リンクファイルをウェブ公開ホルダーに生成しておきます。

無事コピーされてるようでしたら、後は簡単です。JSONとJavascriptは相性バツグンです。

ウェブサーバ側にデータがあるので、クロスドメインも気にする事もありません。

HTMLコードは、ブラウザから見れるので、ここで紹介する事もないですけども。

var URL_DATA_PLAYER = ‘p.json’;
var URL_DATA_DEATH = ‘d.json’;
var URL_RANK_BOOK = ‘rb.json’;
var URL_RANK_DEATH = ‘rd.json’;
var URL_RANK_KILL = ‘rk.json’;
var URL_RANK_MONEY = ‘rm.json’;
var URL_RANK_SCORE = ‘rs.json’;
var URL_CONNECTED = ‘cn.json’;
function getJSON(urlServer,fncCall){
     var req = new XMLHttpRequest();
     req.onreadystatechange = function() {
         if(req.readyState == 4 && req.status == 200){
             var data = JSON.parse(req.responseText);
             fncCall(data);
         }
     };
     req.open(“GET”,urlServer,false);
     req.send(null);
}

お約束のコードを書いたら。後は、受信後の部分。

ある程度の加工は、このタイミングで実施します。

$(document).ready(function(){
     getJSON(URL_DATA_PLAYER,
     function(res){
     // Modify data
     res.forEach(function(obj){
         if(obj[‘alive’] == null){obj[‘alive’]=””;}
         else{obj[‘alive’]=obj[‘alive’]-obj[‘last’]+1;};
         var str = obj[‘w’];
         if(str == null){str=””;};
         str = str.replace(/^Exile_Weapon_/,”);
         str = str.replace(/^gac_JSDF_W_R_/,”);
         str = str.replace(/^srifle_/,”);
         str = str.replace(/^arifle_/,”);
         str = str.replace(/_F$/,”);
         obj[‘w’] = str;
         if(obj[‘damage’] == 0){obj[‘damage’]=”無”;};
     });

マップ上に、マッピングする方法も簡単に実装してます。

res.forEach(function(obj){
     var x = obj[‘x’];
     var y = obj[‘y’];
     if(x!=null && y!=null){
         // Marker
         $(‘#s_map’).append(
             “<div style=’position:absolute;left:”+(x/40-12)+”px;bottom:”+(y/40-12)+”px;’><img src=’images/duck.png’><div>”
             );
     };
});

プレーヤーの情報リストはこんな感じで書いてます。Microsoft .Netのデータバインディングの感じで、簡単に設定できます。なお、この段階まで来ると値の加工は出来ません(フィルターは可能)

※eisenbraun-columns

    // Update Player Deail List
     $(‘#columns’).columns({
         data:res,
         schema: [
         {“header”:”武器”, “key”:”w”,”template”:”{{#w}}<strong>{{w}}</strong>{{/w}}”},
         {“header”:”生存”, “key”:”alive”,”template”:”{{#alive}}{{alive}}{{/alive}}”},
         {“header”:”デス数”, “key”:”deaths”},
         {“header”:”キル数”, “key”:”kills”},
         {“header”:”リスペクト”, “key”:”score”},
         {“header”:”負傷”,”key”:”damage”,”template”:”{{#damage}}{{damage}}{{/damage}}”},
         {“header”:”空腹”, “key”:”hunger”,”template”:”{{#hunger}}{{hunger}}{{/hunger}}”},
         {“header”:”水”, “key”:”thirst”,”template”:”{{#thirst}}{{thirst}}{{/thirst}}”},
         {“header”:”所持金”, “key”:”money”,”template”:”{{#money}}{{money}}{{/money}}”},
         {“header”:”接続数”, “key”:”tc”},
         {“header”:”最近”, “key”:”last”}
         ]
     })
     });

なお、JSON結果をそのまんま貼り付けるだけなら、これで結構です。

    getJSON(’JSONファイル’,
     function(res){
         $(‘どこかのDIV‘).columns({data:res})
     });

データの塊だけでなく、以下は、任意のデータを出力したい場合などの例。

現在「接続してるプレーヤー」、「更新時間」の表示を行ってます。

    getJSON(URL_CONNECTED,
     function(res){
         var dt = new Date(res[0][‘tm’]);
         $(‘#apply_time’).text((dt.getMonth()+1)+”/”+dt.getDate()+” “+dt.getHours()+”:”+dt.getMinutes());
         $(‘#connected’).text(res[0][‘connected’]);
     });

量的に全てを公開できないので、分からない方はぜひともご連絡ください。

nabekのなぬげなblog(゚∀゚)!