OSDN Git Service

ef5a57120e77191de41a763a967d0cc372f0b8d7
[wolvez/wolvez.git] / trunk / lib / ms / ms.rb
1 $KCODE = 'u'
2 require 'logger'
3 $logger = Logger.new('db/debug.log')
4 $logger.progname = "wtls"
5
6 class CWolf
7         def get_ronly_vil(vid)
8                 @vildb.transaction(true) { @vildb['root'] }
9         end
10
11         def get_vil(vid)
12                 @vildb['root']
13         end
14
15         def initialize
16                 @req = CGI.new()
17                 if File.exist?('db/.dontdelete')
18                         @vid = @req['vid'].to_i
19                         @vildb = Store.new("db/vil/#{@vid}.db")
20                         $logger = Logger.new('db/debug.log.' + @vid.to_s)
21                         $logger.progname = "wtls"
22                         # $logger.level = Logger::INFO
23                 end
24                 @headered = false
25                 @head = "Content-Type: text/html; charset=utf-8\r\n"
26         end
27
28         def handle_make
29                 raise ErrorMsg.new('ログインして下さい') if !@login.login
30                 raise ErrorMsg.new('村の名前を指定して下さい') if (@req['name'].to_s == '')
31                 raise ErrorMsg.new('村の名前が長すぎます') if (@req['name'].to_s.size > 60)
32
33                 _build_village(CGI.escapeHTML(@req['name']))
34         end
35
36         def build_village
37                 names = IO.readlines(S[:vilnames_path])
38                 name = names[rand(names.size)].chomp
39
40                 _build_village(name)
41         end
42
43         def _build_village(vilname)
44                 now = Time.now
45                 update_time = Time.mktime(00, 00, now.hour,
46                         now.day, now.month, now.year, nil, nil, nil, nil)
47                 period = 1 * 60 * 20
48
49                 while update_time < now
50                         update_time += period
51                 end
52                 update_time += period / 2 if update_time - (period / 2) < now
53
54                 begin
55                         vldb = Store.new('db/vil.db')
56                         vid = vldb.transaction do
57                                 vid = vldb['recent_vid'].to_i + 1
58                                 vldb['recent_vid'] = vid
59
60                                 vil = Hash.new
61                                 vil['name'] = vilname
62                                 vil['vid'] = vid
63                                 vil['state'] = 0
64                                 vil['start'] = update_time
65                                 vldb['root'].push(vil)
66
67                                 vid
68                         end
69
70                         @vildb = Store.new("db/vil/#{vid}.db")
71                         vil = @vildb.transaction {
72                                 vil = Vil.new(vilname, vid, @login.userid, update_time)
73                                 @vildb['root'] = vil
74                                 vil.start()
75                                 vil
76                         }
77                 end
78         end
79
80         def handle_post
81                 raise ErrorMsg.new("不正な村IDです") if (@vid == 0)
82
83                 pid = @req['pid']
84                 raise ErrorMsg.new("不正なキャラクターです") if (!pid)
85                 pid = pid.to_i
86
87                 msg = trunc(@req['message'])
88                 type = @req['mtype']
89
90                 return if (!msg || msg == '')
91
92                 msg = CGI.escapeHTML(msg).rstrip
93                 msg.gsub!(/\r\n/, '<br>')
94                 msg.gsub!(/[\r\n]/, '<br>')
95                 msg.gsub!(/\n/, '<br>')
96                 msg.gsub!(/[\n]/, '<br>')
97
98                 @vildb.transaction do
99                         vil = get_vil(@vid)
100
101                         raise ErrorMsg.new('終了しています') if (vil.state == 4)
102
103                         player = vil.players.player(@login)
104                         raise ErrorMsg.new("あなた(#{@login.userid})はこの村(#{vil.vid})に参加していません(#{@login.login.inspect})") if !player
105                         raise ErrorMsg.new("不正なキャラクターです") if (player.pid != pid)
106
107                         if (!player.can_whisper && type == 'whisper')
108                                 raise ErrorMsg.new('あなたはささやけません')
109                         end
110                         if (vil.phase == Vil::Phase::Sun && type == 'night')
111                                 raise ErrorMsg.new('既に夜が明けています')
112                         end
113
114                         $logger.debug('post by ' + @login.userid + "(#{pid})")
115                         vil.record(type, player, msg)
116                 end
117         end
118
119         def handle_entry
120                 raise ErrorMsg.new("不正な村IDです") if (@vid == 0)
121
122                 pid = @req['pid']
123                 raise ErrorMsg.new("不正なキャラクターです") if (!pid)
124                 pid = pid.to_i
125
126                 msg = trunc(@req['message'])
127                 raise ErrorMsg.new("発言が空です") if (!msg || msg == '')
128                 raise ErrorMsg.new("発言が長すぎます") if (msg.size > 400)
129
130                 @vildb.transaction {
131                         vil = get_vil(@vid)
132
133                         if vil.players.has_key?(@login.userid)
134                                 raise ErrorMsg.new("エントリー済みです")
135                         elsif (vil.state != 0 && vil.state != 1)
136                                 raise ErrorMsg.new('既に開始しています')
137                         elsif vil.players.max == vil.players.size
138                                 raise ErrorMsg.new("定員に達しています")
139                         elsif vil.players.player(pid)
140                                 raise ErrorMsg.new("既に使用されているキャラクターです")
141                         end
142
143                         @login.registaddress(@req)
144
145                         skill = -1
146                         player = Player.new(pid, @login.userid, skill)
147                         m = CGI::escapeHTML(msg).gsub(/\r\n/, '<br>').gsub(/[\r\n]/, '<br>')
148
149                         vil.add_player(player, m)
150                 }
151         end
152
153         def handle_exit
154                 raise ErrorMsg.new("不正な村IDです") if (@vid == 0)
155
156                 pid = @req['pid']
157                 raise ErrorMsg.new("不正なキャラクターです") if (!pid)
158                 pid = pid.to_i
159
160                 @vildb.transaction {
161                         vil = get_vil(@vid)
162                         if (!vil.players.key?(@login.userid))
163                                 raise ErrorMsg.new("エントリーしていません")
164                         elsif (vil.date == 1)
165                                 raise ErrorMsg.new("既に開始しています")
166                         end
167
168                         vil.delete_player(vil.players[@login.userid])
169                 }
170         end
171
172         def handle_update
173                 move = @vildb.transaction do
174                         vil = get_vil(@vid)
175                         vil.update()
176                         (vil.state == 4)
177                 end
178
179                 if (move)
180                         print "Status: 302 Moved Temporary\n"
181                         print "Location: log_#{@vid}_#{0}.html\n\n"
182                         exit(0)
183                 end
184         end
185
186         def handle_vote
187                 raise ErrorMsg.new('対象がセットされていません') if (!@req['pid'])
188                 @vildb.transaction {
189                         vil = get_vil(@vid)
190                         pl = vil.players.player(@login)
191                         if vil.voting.first() == pl
192                                 if (vil.phase == Vil::Phase::Room && pl.yesterday_mate.index(@req['pid'].to_i))
193                                         raise ErrorMsg.new('昨日同室した相手は指定できません')
194                                 end
195                                 pl.vote = @req['pid'].to_i
196                                 target = vil.players.player(pl.vote)
197                                 raise ErrorMsg.new('相手が既に死亡しています') if target.dead?
198                                 $logger.debug(%Q(#{pl.name} % #{pl.vote}))
199                                 vil.update()
200                         else
201                                 raise ErrorMsg.new('あなたの順番ではありません')
202                         end
203                 }
204         end
205
206         def handle_prevote
207                 raise ErrorMsg.new('投票先がセットされていません') if (!@req['pid'])
208                 yesorno = @req['pid'] =='approve' ? true : false
209                 @vildb.transaction {
210                         vil = get_vil(@vid)
211                         pl = vil.players.player(@login)
212                         if vil.voting.first() == pl
213                                 pl.prevote = yesorno
214                                 vil.update()
215                         else
216                                 raise ErrorMsg.new('あなたの順番ではありません')
217                         end
218                 }
219         end
220
221         def handle_skill
222                 target = (@req['pid'] != '') ? @req['pid'].to_i : nil
223
224                 @vildb.transaction {
225                         vil = get_vil(@vid)
226                         player = vil.players.player(@login)
227                         player.skill_set(vil, target)
228                 }
229         end
230
231         def handle_commit
232                 @vildb.transaction {
233                         vil = get_vil(@vid)
234                         player = vil.players.player(@login)
235                         if (vil.vtargets.index(player) && vil.voting.first == player)
236                                 vil.update()
237                         end
238                 }
239         end
240
241         def handle_cmd
242                 cmd = @req['cmd']
243                 cmds = %w(make entry post update prevote vote skill exit commit)
244                 __send__('handle_' + cmd) if cmds.index(cmd)
245
246                 comet_cmds = %w(entry post update prevote vote skill exit commit)
247                 limitcheck_cmds = %w(post update exit commit)
248                 if comet_cmds.index(cmd)
249                         t = Time.now()
250                         limit = (limitcheck_cmds.index(cmd)) ? S[:processlimit] : false
251                         Duet::Server.dispatch(S[:ajaxdb_path] + ".#{@req['vid']}", limit)
252                         $logger.debug("total time(#{cmd}):" + (Time.now() - t).to_f.to_s)
253                 end
254         end
255
256         def handle_index
257                 changes = INFO.dup
258                 villages = DB::Villages.select {|v| v['state'] != 4 }
259                 active_villages = DB::Villages.select {|v| v['state'] != 4}
260                 if (S[:autobuildvillage] && active_villages.size < S[:autobuildlimit])
261                         build_village()
262                 end
263
264                 print Page::Index.new(@login.form, changes, villages).result()
265         end
266
267         def handle_mkvil
268                 print Page::MakeVillage.new(@login.form).result()
269         end
270
271         def handle_chars
272                 print Page::CharList.new(@login.form, NAMES.dup).result()
273         end
274
275         def handle_history
276                 print Page::History.new(@login.form, HISTORY.dup).result()
277         end
278
279         def handle_info
280                 print Page::Info.new(@login.form, INFO.dup).result()
281         end
282
283         def handle_reg
284                 #print Page::Regulation.new(@login.form).result() mod 2008/11/08 tkt: hide reg
285                 raise ErrorMsg.new("編成は秘密です。")
286         end
287
288         def handle_doc
289                 print Page::Document.new(@login.form).result()
290         end
291
292         def handle_log
293                 print Page::LogIndex.new(@login.form).result()
294         end
295
296         def handle_vid
297                 @vil = get_ronly_vil(@vid)
298
299                 if @vil.state == Vil::State::End
300                         # Vil::Sweeper.sweep(@vil)
301                         url = S[:log_dir] + @vil.vid.to_s + '/0.html'
302                         print '<html><head>'
303                         print %Q(<meta http-equiv="Refresh" content="0;URL=#{url}">)
304                         print '</head><body>'
305                         print %Q(Redirecting to <a href="#{url}">#{url}</a>)
306                         print '</body></html>'
307                         exit
308                 end
309
310                 if @vil.need_sync?
311                         @vildb.transaction {
312                                 @vil = get_vil(@vid)
313                                 @vil.sync() if @vil.need_sync?
314                         }
315                 end
316
317                 date = (@req.key?('date')) ? @req['date'].to_i : @vil.date
318
319                 @player = @vil.players.player(@login)
320                 if (@vil.state != Vil::State::End && @player && date == @vil.date)
321                 # OK I'm foolish, too long.
322                         if @req['cmd'] == 'sync'
323                                 sleep(S[:syncsleeptime]) unless S[:highperformance]
324
325                                 require 'json/lexer'
326
327                                 head = "Content-Type: application/x-javascript; charset=utf-8\r\n"
328                                 mid = @req['mid']
329                                 actstate = @req['ast']
330                                 livestate = @req['lst']
331                                 page =  ActivePage::SyncDay.new(@vil, @player, mid, livestate)
332                                 timeline = ''
333                                 date_forward = false
334
335                                 if page.modified?  # => not wait
336                                         $logger.debug('page modified, not wait')
337                                         if (page.nonmatch? || page.livestate_unmatch?)
338                                                 random_wait()
339                                                 date_forward = true
340                                                 mid = 0
341                                         else
342                                                 sleep(S[:syncsleeptime]) unless S[:highperformance]
343                                         end
344                                         vil = get_ronly_vil(@vid)
345
346                                         player = vil.players.player(@login)
347                                         actbox = ActivePage::ActionBalloon.new(@login.form(vil.vid), player, vil)
348                                         page =  ActivePage::SyncDay.new(vil, player, mid, livestate)
349                                         if page.need_actbox_resync? && (actbox.result?(actstate) || date_forward)
350                                                 act = actbox.sync_result()
351                                         else
352                                                 act = ''
353                                         end
354                                         timeline = ActivePage::TimeLine.new(@vil).result() if date_forward
355
356                                         page_res = page.result()
357                                         size = page.discussions.size
358                                         order = ActivePage::Order.new(vil).sync_result()
359                                         whis = ActivePage::SkillWhisper.new(vil, player).sync_result()
360
361                                         print head + "\r\n" + \
362                                                 [actbox.state, act, page_res, size, order, page.bgimage, timeline, whis].to_json
363
364                                 else  # => wait
365
366                                         dbpath = S[:ajaxdb_path] + ".#{@vil.vid}"
367                                         server = Duet::Server.new(@login.userid, @req, dbpath)
368                                         wait_time = @vil.update_time - Time.now()
369                                         if wait_time > S[:reconnect_sec]
370                                                 wait_time = S[:reconnect_sec]
371                                         elsif wait_time < 0
372                                                 wait_time = 2 
373                                         end
374                                         server.wait(wait_time + 1)  # +1 is helper too wide band-width
375
376                                         if server.wakeup_bysignal
377                                                 t = Time.now
378                                                 # village resync.
379                                                 vil = get_ronly_vil(@vid)
380                                                 player = vil.players.player(@login)
381                                                 page =  ActivePage::SyncDay.new(vil, player, mid, livestate)
382
383                                                 if (player && page.modified?)
384                                                         # undefined player when use opera && wakeup by handle_exit
385
386                                                         if (page.nonmatch? || page.livestate_unmatch?)
387                                                                 random_wait()
388                                                                 date_forward = true
389                                                                 mid = 0
390                                                                 page =  ActivePage::SyncDay.new(vil, player, mid, livestate)
391                                                         end
392
393                                                         actbox = ActivePage::ActionBalloon.new(@login.form(vil.vid), player, vil)
394                                                         if page.need_actbox_resync? && \
395                                                                 (actbox.result?(actstate) || @player.dead != player.dead || date_forward)
396
397                                                                 act = actbox.sync_result()
398                                                         else
399                                                                 act = ''
400                                                         end
401                                                         timeline = ActivePage::TimeLine.new(@vil).result() if date_forward
402
403                                                         page_res = page.result()
404                                                         size = page.discussions.size
405                                                         order = ActivePage::Order.new(vil).sync_result()
406                                                         whis = ActivePage::SkillWhisper.new(vil, player).sync_result()
407
408                                                         print head + "\r\n" + \
409                                                                 [actbox.state, act, page_res, size, order, page.bgimage, timeline, whis].to_json
410                                                         $logger.debug('total time(wakeup by signal):' + (Time.now - t).to_f.to_s)
411                                                 else
412                                                         print page.not_modified()
413                                                 end
414                                         else
415                                                 print page.not_modified()
416                                         end
417                                 end
418
419                                 exit
420                         else
421                                 print ActivePage::VilHeader.new(@vil).result()
422                                 print ActivePage::ActionBalloon.new(@login.form(@vil.vid), @player, @vil).result()
423                                 print ActivePage::Order.new(@vil).result()
424                                 print ActivePage::SkillWhisper.new(@vil, @player).result()
425                                 print ActivePage::Day.new(@vil, @player).result()
426                         end
427                 elsif (@vil.state == Vil::State::End && @req['cmd'] && @req['cmd'] == 'sync')
428                         require 'json/lexer'
429
430                         head = "Content-Type: application/x-javascript; charset=utf-8\r\n"
431                         random_wait()
432                         print head + "\r\n" + \
433                                 ['', '', '', 'reload', '', '', ''].to_json
434                 else
435                         print Page::Day.new(@login.form(@vil.vid), @vil, date, @player).result()
436
437                         if (@vil.date == date && @vil.state != Vil::State::End)
438                                 print Page::Order.new(@login, @vil).result()
439                         end
440                         print %Q(</div>\n)
441
442                         if (@login.login && @vil.state == Vil::State::Welcome && date == @vil.date)
443                                 print Page::ActionBalloon.new(@player, @login, @vil).result()
444                         elsif date + 1 == @vil.date
445                                 print "<a href=\"?vid=#{@vid}\">次の日へ</a>\n"
446                         elsif date != @vil.date
447                                 print "<a href=\"?vid=#{@vid};date=#{date+1}\">次の日へ</a>\n"
448                         end
449
450                         print FOOT + "</div></body></html>"
451                 end
452         end
453
454         def run
455                 if File.exist?('db/.dontdelete')
456                         _run()
457                 else
458                         Dir.mkdir('db') unless File.exist?('db')
459                         Dir.mkdir('db/vil') unless File.exist?('db/vil')
460                         
461                         db = PStore.new('db/vil.db')
462                         db.transaction {
463                           db['root'] = Array.new
464                           db['recent_vid'] = 0
465                         }
466                         
467                         File.open('db/.dontdelete', 'w') {|fh| fh.write '' }
468
469                         redirect()
470                         exit
471                 end
472         end
473
474         def _run
475                 begin
476                         @login = Login.new(@req)
477                         @head += "Set-Cookie: #{@login.cookie}\r\n" if @login.cookie
478
479                         if (!@login.cookie && ENV['REQUEST_METHOD'] == 'POST')
480                                 handle_cmd()
481                                 
482                                 if %w(skill post vote prevote).index(@req['cmd'])
483                                         print "Status: 200 OK\n\n"
484                                 else
485                                         redirect()
486                                 end
487
488                                 return
489                         end
490
491                         if @req['cmd'] != 'sync'
492                                 print @head + "\r\n"
493                                 @headered = true
494                         end
495
496                         cmd = @req['cmd']
497                         if %w(mkvil log doc history info chars reg).index(cmd)
498                                 __send__('handle_' + cmd)
499                         elsif (@vid != 0)
500                                 handle_vid()
501                         else
502                                 handle_index()
503                         end
504                 rescue
505                         unless @headered
506                                 print "Status: 500 Internal Server Error\n"
507                                 print @head + "\r\n"
508                         end
509                         handle_error($!)
510                 end
511         end
512
513         def random_wait
514                 sec = rand(3) + rand()
515                 $logger.debug([@login.userid, sec])
516                 sleep(sec)
517         end
518
519         def redirect
520                 location = (@vid && @vid != 0) ? "?vid=#{@vid}\n\n" : ".\n\n"
521
522                 print "Status: 302 Moved Temporary\n"
523                 print "Location: #{location}"
524         end
525
526         def handle_error(error)
527                 if error.class == ErrorMsg
528                         print error
529                         File.open(S[:fatallog_path], 'a') {|fh| fh.write "\n#{Time.now.to_s}\n#{error}\n" }
530                 else
531                         er  = CGI.escapeHTML("#{error.to_s}\n")
532                         er << CGI.escapeHTML("#{error.backtrace.join("\n")}\n")
533                         print "<pre>\n#{er}</pre>\n"
534                         File.open(S[:fatallog_path], 'a') {|fh| fh.write "\n#{Time.now.to_s}\n#{er}\n" }
535                 end
536         end
537 end